13

Why Scala introduces lazy parameters. Shouldn't it be managed by the JVM (invisible for the user) how the value is initialized? What is the real world use case in which it is worth to give the control into developers hand and define values as lazy?

2
  • 2
    Do you mean lazy val a: Int? Or may be def (a: => Int) (by-name parameters)? Commented Nov 13, 2013 at 14:20
  • Both. Is there a difference? Commented Nov 13, 2013 at 14:23

4 Answers 4

13

The by-name parameters: one of the primary motivations was to support dsls. They allow you to have a really nice syntax in APIs, that almost feel as if they're built into the language. For example, you can very easily define your own custom repeat-loop:

def repeat(body: =>Unit)(until: =>Boolean): Unit = {
  body
  if (until) {} else repeat(body)(until)
}

And then use it as if it were a part of the language.

var i = 0
repeat {
  println(i)
  i += 1
} (i < 3)

Or you could similarly spawn a new thread like this: spawn { println("on the new thread!") }, or you could do automatic resource management of your FileInputStreams like this: withFile("/home/john/.bashrc") { println(_.contents) }.

The lazy values - the motivations here are:

  1. lazy data-structures like Streams that are popular in functional languages that you can use to implement efficient data-structure a-la Okasaki's functional queues.
  2. to avoid allocating or initializing some expensive resources if they're never used in some object, e.g. file handles or database connections.
  3. to initialize objects fields in the correct order, for objects composed of many mixins.
  4. to achieve a correct "initialize only once" semantics when there are many threads sharing a single value (see introduction here).
  5. to have a translation scheme for nested singleton objects:

class A { object B }

becomes something like:

class A {
  class A$B$
  lazy val B = new A$B$
}
Sign up to request clarification or add additional context in comments.

Comments

9

One common scenario is when the writer of a class does not know whether an expensive-to-initialize val will be used. In this case, the val is initialized on demand.

Another scenario is to organically control sequencing of initialization. Often an object is created long before a particular val can be initialized, because other classes haven't been initialized yet. In this case, laziness provides a convenient way for this sequencing to occur naturally, without the author coming up with a Master Plan that sequences a complex, multiphase initialization.

Comments

6

TLDR: because it freaks user out and due to performance reasons

Most of the today's languages are eager. Some of them are not and they called lazy. While many programming problems could be expressed in a beautiful and concise way through lazy evaluation, I don't think having absolute lazyness is a good idea. From subjective perspective programmers are used to think in a eager way (especially those who come from imperative lands) so naively written program in, say, Haskell may confuse you a lot. Having only forks for every possible dish is not so good as having a choice between fork and spoon and although scala support lazy evaluation on language level it defaults to eager model. The reason (besides personal choice of Martin and other language designers) is interop between Java and Scala -- it would be a nightmare to compose this two worlds in one language. Moreover, at the time of Scala design JVM was not there yet to support such features and more or less performant lazy vals were made possible only with introduction of method handles in Java 7 (just two years ago, whereas scala is there for a decade).

Comments

4

I will answer my own question. So one use case when lazy values are extremely useful is if you want to create an immutable data structure with cycles. What is not easy possible without laziness because otherwise you would have to modify an object which is already created. This is not possible if you want your objects were immutable. Let me use as an example the simple cycle implementation.

enter image description here

So in Scala you could implement this in the following way

class Node(inNode: => Node) { lazy val in = inNode }

lazy val node :Node = new Node(new Node(node))

This way you created an immutable cycle. You can verify the result by comparing the references.

scala> node.in
res3: Node = Node@2d928643

scala> node.in.in
res4: Node = Node@3a5ed7a6

scala> node
res5: Node = Node@3a5ed7a6

Comments

Your Answer

Draft saved
Draft discarded

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.