Lecture 1: Course Overview and Introduction to Alloy
February 6, 2001
Lecturer: Daniel Jackson
Please use the email address dnj+6898 at mit.edu to contact Daniel, to help him organize mail for the course.
The following tasks are expected to be completed by the student. They are described in more detail on the course webpage.
It is fine to discuss solutions in a group, but all work that is handed in must be written individually.
This course will focus on advanced topics of software design. We will concentrate on how to express, analyze, and realize high-level design ideas, and will examine current techniques, languages, tools, and methods in the field. We will try to bring together perspectives from different communities, including:
Daniel gave a disclaimer as to his own biases in software design. They include:
There are four main topics planned for the course, although they are flexible:
We'll discuss the Alloy language at the beginning of this class, both as a foundation for discussing design concepts later, and as a means for understanding a modeling language. Alloy has some operators that are generalized versions of normal operators, which makes it very succinct and expressive.
The high-level ideas and properties of Alloy are as follows:
An atom is the basic modeling object in Alloy. An atom is:
Every atom in Alloy belongs to a single basic type. That is, there is no subtyping in Alloy, and all basic types are disjoint. We write basic types in capital letters:
The types of atoms are not declared, but inferred by Alloy from the types of relations (see below) that are used with the atoms.
Every expression in Alloy is a relation. A relation is a set of tuples, where each element of the tuple is of a basic type. One can visualize a relation as a fixed-width table with one or more columns and zero or more rows; each row is a tuple of the relation. All of the entries in a given column must have the same basic type. As in other applications, the number of columns is called the arity of the relation, and the number of rows is called the cardinality.
Alloy allows arbitrary k-relations (the arity can be any natural number). We'll refer to 1-relations as unary, 2-relations as binary, and 3-relations as ternary. Sometimes people use the word ``relation'' to describe only binary relations. We'll consider sets to be unary relations, as they are just an unordered list of atoms.
We are going to define some relations. First we give their types:
spouse: <PERSON, PERSON> sibling: <PERSON, PERSON> Daniel: <PERSON> Tim: <PERSON> Claudia: <PERSON> Emily: <PERSON>Each of the relations above is a variable. Daniel is a unary relation (which we also refer to as a set), and in this case we presume that it contains only one element (i.e., it is is a singleton)-we use the word scalar to refer to singleton sets. We could say that Daniel is an instance of the DANIEL atom. Atoms are never declared explicitly; Alloy is concerned only with variables, and it ``generates'' atoms.
Now let us specify some values for the relations above:
spouse: { (Daniel, Claudia), (Claudia, Daniel), (Tim, Emily), (Emily, Tim) } sibling: { (Daniel, Tim), (Tim, Daniel), (Claudia, Emily), (Emily, Claudia) }As depicted in the figure below, these relations illustrate the lecturer's remark: ``If you didn't know me, you wouldn't know that I'm a living example of a commuting diagram!''
To get a taste of Alloy, we can express this remark in the language:
Some ( Daniel.spouse.sibling & Daniel.sibling.spouse )This states that the intersection between Daniel's spouse's siblings and his sibling's spouses is non-empty.
table: <ROUTER, IP, LINK>
Type | Symbol | Meaning |
Set | + | union |
| - | difference |
| & | intersection |
Comparison | in | subset |
= | equality (have same set of tuples) | |
Relational | ~ | transpose |
-> | product | |
. | join | |
Some of the binary operators that Alloy supports are shown in Table 1. A few of the operators deserve further explanation:
The transpose operation is only defined for binary relations. So, for example, ~parents = child .
The product operation is defined as follows:
      [[p -> q]] = {  (s1, ..., sn, t1, ..., tm) | (s1, ..., sn) in [[p]]   and   (t1, ..., tm) in [[q]]   }
Roughly speaking, the product operation takes all combinations of elements in p followed by elements in q. For example, if we have the following relations:
Knows: <PERSON, PROGRAMMING-LANGUAGE> MITGrad: <PERSON> LISP-Dialect: <PROGRAMMING-LANGUAGE>then the assertion MITGrad->LISP-Dialect would mean that all MIT graduates know all dialects of LISP.
The join operation is defined as follows:
      [[p.q]] = {  (s1, ..., sn-1, t2, ..., tm) | (s1, ..., sn) in [[p]]   and   (t1, ..., tm) in [[q]]   and sn=t1   }
That is, the join operation ``links together'' tuples from p and q that have matching ends, and the element that matches is omitted from the linked set. For example, if we have the following relations:
room: <STUDENT, ROOMS> phone: <ROOM, PHONE>with the following values:
room: { (s1, r1), (s2, r1), (s3, r2), (s4, r3) } phone: { (r1, p1), (r2, p2) }Then the expression room.phone gives you { (s1, p1), (s2, p1), (s3, p2) }, as illustrated by the following diagram:
As an example of the join operator, we can note that Daniel.parents = ~parents.Daniel .
Before we can define the closure operator, we need some expressions for constructing standard expressions in Alloy. These expressions are as follows:
Expression | Type Signature | Meaning |
none[r] | < t1, t2 >   ->   < t1, t2 > | Given a relation r, returns the empty relation {} within the same domain as r |
iden[t] | t   ->   < t, t > | Given an expression of basic type t, returns the relation {(a, a) | a in t} - that is, the relation that maps each element to itself. |
univ[r] | < t1, t2) >   ->   < t1, t2 > | Given a relation r: < t1, t2 >, returns the relation {(a,b)| a in t1, b in t2} - that is, the universal relation that maps every element of type t1 to every element of type t2. |
In the use of these constants in the following sections, we are slightly sloppy about the arguments that are passed to the constructors. This issue will become clear in the following lectures once we talk about declaring signatures.
We can now define the closure operators:
Operator | Meaning |
^r | r + r.r + r.r.r + ... |
*r | iden[r] + ^r |
|
For example, ancestor = ^parent .
Let's compare the use of the join operator as a navigator for relations with the navigation of Java fields and methods via the same syntax. In both Alloy and Java, you could write something like x.f.g . The difference is that in Alloy, x could map to either none, one, or many variables, whereas in Java it can map to either one or none (if the object is null).
In addition, Alloy allows branching and iterative constructors as part of join operations, which further tightens the syntax. An example of branching is Daniel.(spouse + sibling), which gives both the spouse and siblings of Daniel. An example of iteration is Daniel.^friend, which gives the entire network of Daniel's friends. As another example, if next is a list, then l.^next gives all the elements of the list. Finally, the following assertion states that there are no cycles in a list:
^next & iden[list] = none[list]
We could consider the following properties of binary relations (we give them with their Alloy syntax):
no a:t | #a.~r > 1
Finally, we consider some examples of binary relations and the properties that these relations might have. In designing software, it is useful to think about these properties before doing an implementation.
folderContents: <FOLDER, MSG>This relation is heterogeneous, which implies that folders can't contain other folders. Also, it is usually injective in most implementations, where a message is only in one folder. However, it wouldn't have to be this way (you could have overlapping folders).
groupContains: <OBJ, OBJ>This is a homogeneous relation. We would expect it to be irreflexive, since groups shouldn't contain each other.
ip: <MACHINE, IP>This relation maps machines to IP addresses. We would expect it to be injective at any given time, since no two machines have the same IP address at once. However, since a time is not specified as part of the tuple, the relation as written could cover many times and thus might not be injective.
canReadDoesBy: <PERSON, PERSON>This relation is expressing if one person can read what another person does. We would expect it to be transitive, but not symmetric.
occludes: <WINDOW, WINDOW>This relation expresses whether or not a window on the screen occludes another. We would expect it to be asymmetric, since windows can't occlude each other. However, it is not transitive (even in one dimension!)
conflicts: <MACHINE, MACHINE>We didn't have time to talk about this one.
links: <URL, URL>We didn't have time to talk about this one.
Next week, we'll look at the Alloy language itself on Monday and use it to consider software idioms on Wednesday.