6.1 Variables
Felix provides two kinds of variables, var
and val
.
6.1.1 var
terms
var a : int ; // uninitialized is ok a = 2; // mutable is ok var b = &a; // references are ok
A var
is a storage location, or object containing a value.
It is addressable and mutable, and its value is stored at the time of declaration.
We say this assignment is evaluated eagerly.
A var
does not need to be initialized, but you may get unexpected results if you don't assign it.
6.1.2 val
terms
val a = 1; // valid val b; // error, not initialized var c = &a; // error, not addressable a = 2; // error, not mutable
A val
is a named expression, it is neither addressable nor mutable, and must be initialised.
Its value is that of its expression at the time of evaluation.
Vals can be evaluated eagerly like a var
, but they may also be evaluated lazily.
Felix converts the val
expression replacing its name with the body of its expression.
6.1.3 Eager and Lazy
When we talk about computation, the words "eager" and "lazy" mean something very specific. The same expression can be computed in two ways. If it is evaluated eagerly, it will be computed to a value as soon as it is declared. If it is evaluated lazily, it will be computed to a value when it is needed.
There are tradeoffs for each, and it is up to the programmer to determine which is better for the task at hand.
If a var
is never used, you waste some effort computing its result.
If a val
is used, you may pay for the cost of computing its result when you don't want to.
And you may pay the cost each time the val
is used.
The primary motivation for using val
is to support optimisation.
The compiler can choose either eager or lazy evaluation depending on what seems to be most efficient.
If the number of uses of the val
is low, lazy evaluation is usally faster.
6.1.4 Indeterminate values
The value represented by a val
can have an initialiser depending on a variable.
When this happens, it is not precisely specified, and we call this indeterminate evaluation.
If the initialising expression may throw an exception or otherwise fail to terminate, evaluation is also indeterminate.
For example, in:
val num = 10; val denom = 0; val quot = num / denom; // division by zero val result = if denom == 0 then 0 else quot; println$ result;
Felix cannot determine if the program will print 0 or fail with a division by zero exception. If the compiler decides to evalulate lazily then the above is equivalent to
val denom = 0; println$ if denom == 0 then 0 else 10 / denom endif;
6.1.5 Forcing lazy evaluation
Lazy evaluation can be enforced by use of closures, denoted with curly braces {...
}.
val num = 10; val denom = 0; val quot = { num / denom }; // defer evaluation val result = if denom == 0 then 0 else #quot // evaluate now if control flows here endif ; println$ result;
0