74

Am I right understanding that

  • def is evaluated every time it gets accessed

  • lazy val is evaluated once it gets accessed

  • val is evaluated once it gets into the execution scope?

tenshi
  • 26,268
  • 8
  • 76
  • 90
Ivan
  • 63,011
  • 101
  • 250
  • 382
  • See http://stackoverflow.com/questions/4437373/use-of-def-val-and-var-in-scala/4440614#4440614 – Jesper Nov 08 '12 at 15:19
  • 1
    Also http://stackoverflow.com/questions/13126104/is-there-any-advantage-to-definining-a-val-over-a-def-in-a-trait – Matthew Farwell Nov 08 '12 at 15:42
  • Note that `val` could not be freely extended. If you have `val a = 5`, `val b = a + 1` in base trait and try to expand it with `val a = 6` then instead of 7 `b` would throw some notdefined error. – ayvango Jan 03 '16 at 12:25

8 Answers8

94

Yes, but there is one nice trick: if you have lazy value, and during first time evaluation it will get an exception, next time you'll try to access it will try to re-evaluate itself.

Here is example:

scala> import io.Source
import io.Source

scala> class Test {
     | lazy val foo = Source.fromFile("./bar.txt").getLines
     | }
defined class Test

scala> val baz = new Test
baz: Test = Test@ea5d87

//right now there is no bar.txt

scala> baz.foo
java.io.FileNotFoundException: ./bar.txt (No such file or directory)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(FileInputStream.java:137)
...

// now I've created empty file named bar.txt
// class instance is the same

scala> baz.foo
res2: Iterator[String] = empty iterator
om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
  • By the way, wouldn't it be a good idea to define all the vals lazy unless the opposite is necessary? – Ivan Feb 27 '12 at 01:24
  • 6
    @Ivan I suspect that this would decrease performance cause lazy vals are [translated in code](http://stackoverflow.com/q/3041253/298389) with double checking in order to provide thread safety. There could be other reasons. – om-nom-nom Feb 27 '12 at 07:14
  • 2
    Ok. Thanks. That's an idea about `lazy val` vs `val`. Another question is `lazy val` vs `def`. I've got an immutable class with some properties being pure functions of the data passed in the constructor. Wouldn't it be a good idea to expose them as lazy vals instead of defs (considering the result is a small piece of data (like an integer value) but takes some time to calculate)? – Ivan Feb 27 '12 at 10:32
  • 4
    @Ivan Just a few days ago I've been introduced to the case, when using lazy vals introduced a deadlock in code. Beware of using lazy vals in multithreaded code. As for your latest questions, I guess yes, it will be not only much performant solution in some circumstances but more readable IMO (since man who will read this code will be awared that this is *init once* piece of code). – om-nom-nom May 24 '12 at 12:45
54

Yes, though for the 3rd one I would say "when that statement is executed", because, for example:

def foo() {
    new {
        val a: Any = sys.error("b is " + b)
        val b: Any = sys.error("a is " + a)
    }
}

This gives "b is null". b is never evaluated and its error is never thrown. But it is in scope as soon as control enters the block.

Owen
  • 38,836
  • 14
  • 95
  • 125
44

I would like to explain the differences through the example that i executed in REPL.I believe this simple example is easier to grasp and explains the conceptual differences.

Here,I am creating a val result1, a lazy val result2 and a def result3 each of which has a type String.

A). val

scala> val result1 = {println("hello val"); "returns val"}
hello val
result1: String = returns val

Here, println is executed because the value of result1 has been computed here. So, now result1 will always refer to its value i.e "returns val".

scala> result1
res0: String = returns val

So, now, you can see that result1 now refers to its value. Note that, the println statement is not executed here because the value for result1 has already been computed when it was executed for the first time. So, now onwards, result1 will always return the same value and println statement will never be executed again because the computation for getting the value of result1 has already been performed.

B). lazy val

scala> lazy val result2 = {println("hello lazy val"); "returns lazy val"}
result2: String = <lazy>

As we can see here, the println statement is not executed here and neither the value has been computed. This is the nature of lazyness.

Now, when i refer to the result2 for the first time, println statement will be executed and value will be computed and assigned.

scala> result2
hello lazy val
res1: String = returns lazy val

Now, when i refer to result2 again, this time around, we will only see the value it holds and the println statement wont be executed. From now on, result2 will simply behave like a val and return its cached value all the time.

scala> result2
res2: String = returns lazy val

C). def

In case of def, the result will have to be computed everytime result3 is called. This is also the main reason that we define methods as def in scala because methods has to compute and return a value everytime it is called inside the program.

scala> def result3 = {println("hello def"); "returns def"}
result3: String

scala> result3
hello def
res3: String = returns def

scala> result3
hello def
res4: String = returns def
oblivion
  • 5,928
  • 3
  • 34
  • 55
11

One good reason for choosing def over val, especially in abstract classes (or in traits that are used to mimic Java's interfaces), is, that you can override a def with a val in subclasses, but not the other way round.

Regarding lazy, there are two things I can see that one should have in mind. The first is that lazy introduces some runtime overhead, but I guess that you would need to benchmark your specific situation to find out whether this actually has a significant impact on the runtime performance. The other problem with lazy is that it possibly delays raising an exception, which might make it harder to reason about your program, because the exception is not thrown upfront but only on first use.

Malte Schwerhoff
  • 12,684
  • 4
  • 41
  • 71
  • The runtime performance needs to be compared between lazy val and def. For a non-trivial function/value, the cost of synch would be overshadowed by the cost of the calculation. WRT hiding exceptions, depending upon the use case, it may be good. For example, in the FifthElement code above, the user could check isEmpty before calling fifthElement and would never see the exception (technically, they should check the size to ensure the sequence has at least five elements), but if val were used, the exception would be thrown as soon as the object is created. – Noel Yap Nov 08 '12 at 23:27
  • While I conceptually agree with your comment, I don't think that your ``firthElement`` is a good example to illustrate it. If you think in terms of pre- & postconditions, then the call ``seq(5)`` is obviously only safe if the sequence contains at least six elements - which is not guaranteed, even if ``isEmpty`` is false. Even worse, it is an obligation that the caller of ``fithElement`` cannot fulfil if they are not the one who created the object in the first place, because they lack are unaware of the length of the sequence. Adding a ``length`` function would make your example valid. – Malte Schwerhoff Nov 09 '12 at 07:58
  • yes, it's a contrived example full of bugs. It's intent is to showcase the differences among val, lazy val, and def. My real-world scenario is of a intersection object in a hexagonal grid. The intersection could be one of two kinds: one with top, left-bottom, and right-bottom adjacent tiles (ie a 'Y' intersection) or one with bottom, left-top, and right-top adjacent tiles (ie an upside-down 'Y' intersection). An exception should be thrown when trying to access an adjacent tile that cannot exist. As such, val cannot be used. – Noel Yap Nov 09 '12 at 14:18
  • I've replaced the isEmpty with hasFifthElement. – Noel Yap Nov 09 '12 at 14:30
7

You are correct. For evidence from the specification:

From "3.3.1 Method Types" (for def):

Parameterless methods name expressions that are re-evaluated each time the parameterless method name is referenced.

From "4.1 Value Declarations and Definitions":

A value definition val x : T = e defines x as a name of the value that results from the evaluation of e.

A lazy value definition evaluates its right hand side e the first time the value is accessed.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
5

def defines a method. When you call the method, the method ofcourse runs.

val defines a value (an immutable variable). The assignment expression is evaluated when the value is initialized.

lazy val defines a value with delayed initialization. It will be initialized when it's first used, so the assignment expression will be evaluated then.

Jesper
  • 202,709
  • 46
  • 318
  • 350
3

A name qualified by def is evaluated by replacing the name and its RHS expression every time the name appears in the program. Therefore, this replacement will be executed every where the name appears in your program.

A name qualified by val is evaluated immediately when control reaches its RHS expression. Therefore, every time the name appears in the expression, it will be seen as the value of this evaluation.

A name qualified by lazy val follows the same policy as that of val qualification with an exception that its RHS will be evaluated only when the control hits the point where the name is used for the first time

kmos.w
  • 422
  • 1
  • 3
  • 13
1

Should point out a potential pitfall in regard to usage of val when working with values not known until runtime.

Take, for example, request: HttpServletRequest

If you were to say:

val foo = request accepts "foo"

You would get a null pointer exception as at the point of initialization of the val, request has no foo (would only be know at runtime).

So, depending on the expense of access/calculation, def or lazy val are then appropriate choices for runtime-determined values; that, or a val that is itself an anonymous function which retrieves runtime data (although the latter seems a bit more edge case)

virtualeyes
  • 11,147
  • 6
  • 56
  • 91