ExpandCollapsePrev Next Index

+ 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
  println$ result;

(2, 1)