Lists, Sets and Tuples categories
Lists, Sets and Instructions
Blocks
Conditionals

Blocks

Parentheses can be used to group a sequence of instructions into one. In this case, the returned value is the value of the last instruction :
 (x := 3x := 5)
Parentheses can also be used to explicitly build an expression. In the case of boolean evaluation (for example in an if), any expression is considered as true except false, empty sets and empty lists :
 (1 + 2* 3
 if (x = 2 & l)
Local variables can be introduced in a block with the let construct. These variables can be typed, but it is not mandatory (CLAIRE will use type inference to provide with a reasonable type). On the other hand, unlike languages such as C++, you always must provide an initialization value when you define a variable. A let instruction contains a sequence of variable definitions and, following the in keyword, a body (another instruction). The scope of the local variable is exactly that body and the value of the let instruction is the value returned by this body.
 let x := 1y := 3 in (z := x + yy := 0)
Notice that CLAIRE uses := to represent assignment and = to represent equality. The compiler will issue a warning if a statement (x = y) is used where an assignment was probably meant (this is the case when the value of the assignment is not needed, such as in x := 1, y = 3, z := 4).

The value of local variables can be changed with the same syntax as an update to an object: the syntax :op is allowed for all operations op :
 x := x + 1x :+ 1x :2x :^ 2
The name of a local variable can be any identifier, including the name of an existing object or variable. In that case, the new variable overrides the older definition within the scope of the let. While this may prove useful in a few cases, it should be used sparingly since it yields to code that is hard to read. A rule of thumb is to avoid mixing the name of variables and the name of properties since it often produces errors that are hard to catch (the property cannot be accessed any more once a variable with the same name is defined). The control structure when is a special form of let, which only evaluates the body if the value of the local variable (unique) is not unknown (otherwise, the returned value is unknown). This is convenient to use slots that are not necessarily defined as in the following example :
 when f := get(father,x)
 in printf("his father is ~S\n"f)
The default behavior when the value is unknown can be specified using the else keyword. The statement following the else keyword will be evaluated and its value will be returned when the value of the local variable is unknown :
 when f := get(father,x)
 in printf("his father is ~S\n"f)
 else printf("his father is not known at the present time\n")
Local variables can also be introduced as a pattern, that is a tuple of variables. In that case, the initial value must be a tuple of the right length. For instance, one could write :
 let (xyz:= tuple(123in x + y + z
The tuple of variable is simply introduced as a sequence of variables surrounded by two parentheses. The most common use of this form is to assign the multiple values returned by a function with range tuple, as we shall see in the next section. If we suppose that f is a method that returns a tuple with arity 2, then the two following forms are equivalent:
 let (x1,x2:= f() in ...

 let l := f(), x1 := l[1], x2 := l[2in ...
[XL] In XL CLAIRE, as a syntactical shortcut, we can define in a single let statement both tuple assigment and normal variable assigment as in :
 let (x1,x2:= f(),
     x3 := g() in ...
Tuples of variables can also be assigned directly within a block as in the following example :
 (x1x2:= tuple(x2x1)
Although this mostly used for assigning the result of tuple-valued functions without any useless allocation, it is interesting to note that the previous example will be compiled into a nice value-exchange interaction without any allocation (the compiler is smart enough to determine that the list "list(x2,x1)" is not used as such).

The key principle of lexical variables is that they are local to the "let" in which they are defined. CLAIRE supports another similar type of block, which is called a temporary slot assignment. The idea is to change the value of a slot but only locally, within a given expression. This is done as follows:
 let x.r := y in e
changes the value of r(x) to y, executes e and then restore r(x) to its previous value. It is strictly equivalent to
 let old_v := x.r
 in (x.r := y,
     let result := e
     in (x.r := old_vresult))
CLAIRE provides automatic type inference for variables that are defined in a let so that explicit typing is not necessary in most of the cases. Here are a few rules to help you decide if you need to add an explicit type to your variable or even cast a special type for the value that is assigned to the variable :

  1. (a) Type inference will provide a type to a Let variable only if they do not have one already.
  2. (b) when you provide a type in let x:t := y, the compiler will check that the value y belong to t and will issue a warning and/or insert a run-time type-check accordingly.
  3. (c) if you want to force the type that is inferred to something smaller than what CLAIRE thinks for y, you must use a cast :
     let x := (y as t2in ...
To summarize :