Thursday, 23 February 2012

Computing in Libretto (3 of 5). Local Variables

All variables in Libretto are local and inaccessible from the global context. The types of Libretto variables are:
  1. Block variables
  2. Path variables
  3. Parameter variables.
The range of local variables is a block or a path, in which they are defined. Similar to fields, local variables can be of two types – mutable (marked by the keyword var) and immutable (marked by fix).


Block Variables

Block variables are defined in blocks, for instance, in the body of a function:
def* size {
  var x = 0
  this. {x = x + 1}
  x
}
Here x is a block variable defined in the body of the function size. The scope of x is limited by the curly brackets of the function body.

Path Variables

The range of local variables introduced by the operators as and index is the path, in which they are defined, and its sub-paths:
(“a”, “b”) as s. (1,2) as n. (a + n)  //  “a1”  “a2”  “b1”  “b2”
The range of the variables s and n is from the step, in which they are introduced, to the rightmost step of the path.

Variable Typing

The block and path variables are type-free. A block variable can be marked by the asterisk *, which means that this variable is able to contain a sequence of values:
{
  var seq* = 2
  seq .= “Marie has”
  seq += “candies”
  seq  //  “Marie has” 2 “candies”
  seq.size  //  3

  var single = (1,2)  // ERROR: too many values in variable single
}
The variable seq can contain an arbitrary number of values, whereas single can have only zero or one value.

Unlike block and path variables, parameter variabels can be explicitly typed. Such typing is a key for polymorphic definitions:
def f(x: Int) = x * x
def f(s: String) = s + s

f(5)  //  25
f(“abc”)  //  “abcabc”

{
  fix x = 5
  fix y = “abc”

  f(x)  //  25
  f(y)  //  “abcabc”
}
The block variables x and y are type-free, but dynamic dispatch manages to find the appropriate definition of the function f, because it is based on the actual types of values stored in variables.

Mutable and Immutable Variables

Variables in Libretto are divided into immutable and mutable variables. The first ones are marked by the keyword fix, and the second – by the keyword var:
{
  var x = 1
  fix y = 1
  x = 2 
  x  //  2
  y = 1  //  ERROR: the attempt to reassign fix variable y
}
Path variables are always immutable, they can not be explicitly modified:
(“a”, “b”) as s. {s = 5} // ERROR: attempt to reassign fix variable s
A sequence in Libretto is not a compound data structure, but a generalized single value. This means that unlike, say, lists (see section Class List) the modification of a sequence is considered as the modification of the variable, which contains this sequence. In particular, sequences in immutable variables can not be modified:
{
  fix xf* = (1,2,3)
  xf += 4  //  ERROR: attempt to reassign fix variable x
} 
If we need to change a sequence, it has to be copied into a mutable variable:
{  
  fix xf* = (1,2,3)
  var xv* = xf
  xv += 4
  xv  //  1 2 3 4
}
Using immutable variables where possible is a good programming style, which provides better-quality code development with a reduced number of bugs.

Variable Initialization

For each kind of variables there is a special initialization method. For block variables an assignment operator is used:
{
  var x = 115
  fix name = “Marie”
}
If a block variable is not initialized explicitly, it is set by default as ():
{
  var x
  if (x) “yes” else “no”
}
  // “no”
Path variables are initialized by the operators as and index:
(1,2,3) as x. (5 + x)  //  6 7 8
(“a”, “b”, “c”) index i. i  //  0 1 2
Parameter variables are initialized in function calls:
def f(x) = x + 1
f(3)  //  4

Parameter Passing in Constructors and Functions

Function parameters are always private immutable variables. They are inaccessible from outside, and their value can not be modified:
def f(n*) {
  n += 6
  n
}
f(1,2,3,4,5)  //  ERROR: Attempt to reassign fixed variable n

def f(n*) {
  var m = n
  m += 6
  m
}
f(1,2,3,4,5)  //  1 2 3 4 5 6
Function parameters can be supplied with default values. If parameters with default values are the rightmost, then they can be omitted in function calls:
def f(n: Int, m: Int = 5) = n + m

f(1, 2)  //  3
f(1)  //  6
Note that inaccurate use of default values in polymorphic definitions can lead to ambiguity and to an error.

Like in functions, constructor parameters (both fields and variables) can also have default values, for instance:
class C(fix prop: String, v: Int = 2) {
  var n: Int = v
  var m: Int = v * v
}
C(“hehe”, 5).m  //  25
C(“haha”).m  //  4
In this example prop is a field and v is a parameter variable. Parameter variables are not marked by var or fix. They play a supporting role in object creation. Unlike fields, which are public by default and can be either mutable or immutable, parameter variables in constructors are always treated as private and immutable. The life of a parameter variable is limited to the constructor execution time. After that, this variable is inaccessible.

Access to Local Variables

The range of a local variable in Libretto is always limited to the block or the path, in which the variable is defined. Thus, variables always behave as private.

In case of ambiguity, when the names of a variable and another entity coincide, the name is always interpreted as the variable:
object obj extends String(“c”)
class C {
  fix field = “ab”
  def f1 = field + obj
  def f2 = {fix field = 5; fix obj = 6; field + obj}
}
C().f1  //  “abc”
C().f2  //  11
If the other entity must be accessed, the operators % and @ are used (see sections Operator @ and Parametric Fields and Operator %). The operator % interpets its argument as an object, and @ interprets its argument as a field:
object obj extends String(“c”)
class C {
  fix field = “ab”
  def f1 = field + obj
  def f2 = {fix field = 5; fix obj = 10; @field + %obj}
}
C().f1  //  “abc”
C().f2  //  “abc”

No comments:

Post a Comment