Iulian Dragos

0

The next Scala release will be equipped with a limited form of laziness: lazy values. The main motivation is initialization of complex (interlinked) objects, and the scala compiler is a prime example. Having values the depend on eachother (some of which might be mixed in) makes is cumbersome (or even impossible) to find the right sequence, so that no uninitialized values are ever accessed.

Example

class A {
  val b = a + 1
  val a = 5

  Console.println(b)
}

The fact that b gets value 1 instead of 6 is surprising, and can be a source if nuisance. If 'a' were declared in another trait T, and A mixed in T, the explanation would be even less evident.

Current proposal

The current design introduces a new modifier, lazy, which can only be applied to values. We also cosidered let instead of lazy val but went for the modifier for now (which one you prefer?). Lazy values can appear everywhere normal values can appear, except for parameter lists. Also, lazy values can only be concrete, and the semantics is that lazy values are initialized on first access (exactly like objects are initialized now). The above example could be rewritten to initialize b to 6:

class A {
  lazy val b = a + 1
  val a = 5

  Console.println(b)
}

Implementation

There are two cases to consider: lazy values that are fields, and lazy values that are local variables. Fields are treated differently because they can be shared between threads, and their initialization has to be thread-safe. Lazy locals are translated to a local function which is looks very similar to a lazy field accessor (except for synchronization). Lazy accessors each use a bit in a bitmap to know if they have been initialized already. The above example would look like:

class A {
    protected var bitmap$0: Int = 0;
    lazy private[this] val b: Int = _;
    def b(): Int = {
      if (A.this.bitmap$0 & 1 == 0) {
          A.this.synchronized {
            if (A.this.bitmap$0 & 1 == 0) {
                A.this.synchronized {
                  A.this.b = A.this.a().+(1);
                };
                A.this.bitmap$0 |= 1;
              };
          }
        }
      A.this.b
    };
  //..
}

Discussion

Lazy values are great, but there are a few points that should be mentioned:

  • there are no guarantees about when (or if at all) the initializer is run. Therefore, they should not perform any side effects.
  • also, depending on (mutable) state is usually a bad idea: not knowing when the initializer is run, one hardly knows what value it will take
  • accessing a value can now lead to non-termination. Currently, plain values can't do that (at worst, the looping bahavior is observed when an object is constructed, if one of its values depends on a looping method), but now they can (for instance, a value might be overriden by a lazy value in a subclass).

The adventurous soul can give lazy values a try already. You can checkout the sources from the lazyval branch, build the compiler (type ant :) ) and have a go. The branch will dissappear once this makes it into the main compiler.

Comments (0) Trackbacks (0)

No comments yet.

Leave a comment








No trackbacks yet.