The call-by-name or call-by-value distinction seems less useful in this case, because neither your x, nor your loop take any arguments.
But there are two other ways in which val and def differ.
First distinction is: eager vs. lazy.
- The right hand side of
val is evaluated eagerly, as soon as the val is defined.
- The right hand side of
def is evaluated lazily, as soon as the
def is accessed.
For example:
def x = { println("x is evaluated"); 42 }
val y = { println("y is evaluated"); 42 }
x
Will print:
y is evaluated
x is evaluated
because y is evaluated immediately, whereas x is evaluated only when we call it.
The other difference is whether the result of the computation is cached or not:
- The value of
val is evaluated once, and then cached
- The value of
def is evaluated every time we call the function
This is why
def dice = { new scala.util.Random().nextInt(6) + 1 }
val once = { new scala.util.Random().nextInt(6) + 1 }
println(dice + " <-> " + once)
println(dice + " <-> " + once)
println(dice + " <-> " + once)
println(dice + " <-> " + once)
will output:
5 <-> 6
4 <-> 6
6 <-> 6
5 <-> 6
that is, dice is a more or less useful random number generator that generates values 1-6, whereas once is a rather useless random value that is fixed once it's created.
Now, you can think of a 2x2-table with eager-vs-lazy in one dimension, and cached-vs-not-cached on the other:
val is eager and cached
lazy val is lazy and cached (so are memoized defs)
def is lazy and uncached
- There is not really eager and uncached, because computing something and then immediately throwing it away is not very useful. Maybe there is one exception that gets pretty close: the entry point of the program, because even though
def main is a def, it always gets called anyway, so in a sense it's both kind-of eager and uncached.
In your code, loop is essentially a tail-recursive version of the non-terminating
def loop: Boolean = { while (true) {}; true }
but since loop is a def, and therefore lazy, the right hand side is not evaluated when you define it.
If you now define
def x = loop
nothing is happening still, because x is also lazy, so the right hand side is again not evaluated.
But if you define
val x = loop
then x is a val, thus its right hand side is evaluated eagerly. On the right hand side of the definition, there is an invocation of loop. Now loop is accessed, and since it is a def, it now evaluates its body, and goes into an infinite loop.