272

I noticed that Scala provide lazy vals. But I don't get what they do.

scala> val x = 15
x: Int = 15

scala> lazy val y = 13
y: Int = <lazy>

scala> x
res0: Int = 15

scala> y
res1: Int = 13

The REPL shows that y is a lazy val, but how is it different from a normal val?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
kiritsuku
  • 52,967
  • 18
  • 114
  • 136

7 Answers7

363

The difference between them is, that a val is executed when it is defined whereas a lazy val is executed when it is accessed the first time.

scala> val x = { println("x"); 15 }
x
x: Int = 15

scala> lazy val y = { println("y"); 13 }
y: Int = <lazy>

scala> x
res2: Int = 15

scala> y
y
res3: Int = 13

scala> y
res4: Int = 13

In contrast to a method (defined with def) a lazy val is executed once and then never again. This can be useful when an operation takes long time to complete and when it is not sure if it is later used.

scala> class X { val x = { Thread.sleep(2000); 15 } }
defined class X

scala> class Y { lazy val y = { Thread.sleep(2000); 13 } }
defined class Y

scala> new X
res5: X = X@262505b7 // we have to wait two seconds to the result

scala> new Y
res6: Y = Y@1555bd22 // this appears immediately

Here, when the values x and y are never used, only x unnecessarily wasting resources. If we suppose that y has no side effects and that we do not know how often it is accessed (never, once, thousands of times) it is useless to declare it as def since we don't want to execute it several times.

If you want to know how lazy vals are implemented, see this question.

Community
  • 1
  • 1
kiritsuku
  • 52,967
  • 18
  • 114
  • 136
  • 69
    As a supplemental: @ViktorKlang posted on Twitter: ["Little known Scala fact: if the initialization of a lazy val throws an exception, it will attempt to reinitialize the val at next access."](https://twitter.com/#!/viktorklang/status/104483846002704384) – Peter Schmitz Sep 20 '11 at 19:27
  • @PeterSchmitz And I find this terrible. Compare with [`Lazy`](https://learn.microsoft.com/en-us/dotnet/framework/performance/lazy-initialization#exceptions-in-lazy-objects) in .NET – Pavel Voronin Aug 02 '18 at 08:51
64

This feature helps not only delaying expensive calculations, but is also useful to construct mutual dependent or cyclic structures. E.g. this leads to an stack overflow:

trait Foo { val foo: Foo }
case class Fee extends Foo { val foo = Faa() }
case class Faa extends Foo { val foo = Fee() }

println(Fee().foo)
//StackOverflowException

But with lazy vals it works fine

trait Foo { val foo: Foo }
case class Fee extends Foo { lazy val foo = Faa() }
case class Faa extends Foo { lazy val foo = Fee() }

println(Fee().foo)
//Faa()
Landei
  • 54,104
  • 13
  • 100
  • 195
  • But it will lead to the same StackOverflowException if your toString method outputs "foo" attribute. Nice example of "lazy" anyway!!! – Fuad Efendi Dec 17 '16 at 00:13
45

A lazy val is most easily understood as a "memoized (no-arg) def".

Like a def, a lazy val is not evaluated until it is invoked. But the result is saved so that subsequent invocations return the saved value. The memoized result takes up space in your data structure, like a val.

As others have mentioned, the use cases for a lazy val are to defer expensive computations until they are needed and store their results, and to solve certain circular dependencies between values.

Lazy vals are in fact implemented more or less as memoized defs. You can read about the details of their implementation here:

http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html

tksfz
  • 2,932
  • 1
  • 23
  • 25
45

I understand that the answer is given but I wrote a simple example to make it easy to understand for beginners like me:

var x = { println("x"); 15 }
lazy val y = { println("y"); x + 1 }
println("-----")
x = 17
println("y is: " + y)

Output of above code is:

x
-----
y
y is: 18

As it can be seen, x is printed when it's initialized, but y is not printed when it's initialized in same way (I have taken x as var intentionally here - to explain when y gets initialized). Next when y is called, it's initialized as well as value of last 'x' is taken into consideration but not the old one.

Hope this helps.

Andrii Abramov
  • 10,019
  • 9
  • 74
  • 96
Mital Pritmani
  • 4,880
  • 8
  • 38
  • 39
19

Also lazy is useful without cyclic dependencies, as in the following code:

abstract class X {
  val x: String
  println ("x is "+x.length)
}

object Y extends X { val x = "Hello" }
Y

Accessing Y will now throw null pointer exception, because x is not yet initialized. The following, however, works fine:

abstract class X {
  val x: String
  println ("x is "+x.length)
}

object Y extends X { lazy val x = "Hello" }
Y

EDIT: the following will also work:

object Y extends { val x = "Hello" } with X 

This is called an "early initializer". See this SO question for more details.

Community
  • 1
  • 1
Jus12
  • 17,824
  • 28
  • 99
  • 157
  • 11
    Can you clarify why the declaration of Y does not immediately initialize the variable "x" in the first example before calling the parent constructor? – Ashoat Mar 26 '13 at 01:47
  • 2
    Because superclass constructor is first one that gets implicitly called. – Stevo Slavić May 12 '14 at 16:27
  • @Ashoat Please see [this link](https://github.com/scala/scala.github.com/blob/master/tutorials/FAQ/initialization-order.md) for an explanation of why it is not initialized. – Jus12 Dec 21 '14 at 07:10
4

A demonstration of lazy - as defined above - execution when defined vs execution when accessed: (using 2.12.7 scala shell)

// compiler says this is ok when it is lazy
scala> lazy val t: Int = t 
t: Int = <lazy>
//however when executed, t recursively calls itself, and causes a StackOverflowError
scala> t             
java.lang.StackOverflowError
...

// when the t is initialized to itself un-lazily, the compiler warns you of the recursive call
scala> val t: Int = t
<console>:12: warning: value t does nothing other than call itself recursively
   val t: Int = t
pjames
  • 192
  • 1
  • 4
  • 13
1
scala> lazy val lazyEight = {
     |   println("I am lazy !")
     |   8
     | }
lazyEight: Int = <lazy>

scala> lazyEight
I am lazy !
res1: Int = 8
  • All vals are initialized during object construction
  • Use lazy keyword to defer initialization until first usage
  • Attention: Lazy vals are not final and therefore might show performance drawbacks