Styne J
Styne J

Reputation: 195

Difference between def and val in Scala

loop is defined as below:

 def loop : Boolean = loop

When x is defined as: def x = loop then "x: Boolean" is shown in console.

and

When x is defined as: val x = loop then it goes to infinite loop

I know def is using call by-name and val is using call by-value. Even though this point about loop defined above is not much clear.

Upvotes: 5

Views: 1395

Answers (2)

Andrey Tyukin
Andrey Tyukin

Reputation: 44918

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.

Upvotes: 9

Steve Waldman
Steve Waldman

Reputation: 14073

def doesn't evaluate the right-hand side of the assignment. Just like

def f(x : Int) = x + 2

doesn't (in this case logically can't) evaluate f of anything, just defines what the function f, means, neither def loop : Boolean = loop nor def x = loop evaluate anything. You are just defining a function to be executed at some other point.

But vals do require the right-hand side of the assignment to be evaluated. So val x = loop tries to execute the expression on the right-hand side. Trying to evaluate loop never terminates, though, since loop is an infinite loop.

Upvotes: 10

Related Questions