When to use lazy values ​​in Scala?

Why Scala introduces lazy options. Shouldn't the JVM (invisible to the user) be controlled, how is the value initialized? What is the real use case in a world in which it is worth giving control over a development team and determining the values ​​as lazy?

+6
source share
4 answers

Parameters by name : one of the main motives was maintaining dsls. They allow you to have really good syntax in APIs that almost feel like they are built into the language. For example, you can easily define your own repeat -loop:

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

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

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

Or you could create a new thread in the same way as follows: spawn { println("on the new thread!") } , Or you could automate the resource management of your FileInputStream as follows: withFile("/home/john/.bashrc") { println(_.contents) } .

The lazy meanings are the motives here:

  • lazy data structures such as Stream s , which are popular in functional languages ​​that can be used to implement an efficient data structure a-la Okasaki functional queues.
  • to avoid allocating or initializing some expensive resources if they are never used in any object, for example. file descriptors or database connections.
  • to initialize the fields of objects in the correct order for objects consisting of many mixins.
  • to achieve the correct semantics of "initialize only once" when there are many threads sharing the same value (see the introduction here ).
  • 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$ } 
+6
source

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

Another scenario is the organic control of initialization sequencing. Often an object is created long before a particular val can be initialized because other classes have not yet been initialized. In this case, laziness provides a convenient way for this sequencing in a natural way, without the author coming up with a Master Plan that sequentially performs complex multiphase initialization.

+7
source

TL; DR: because it issues users due to performance reasons

Most modern languages ​​are impatient. Some of them are not, and they are called lazy. Although many programming problems can be expressed in a beautiful and concise way through a lazy assessment, I do not think that absolute laziness is a good idea. From a subjective point of view, programmers are used to thinking with impatience (especially those who come from imperative lands), so a naively written program, say, in Haskell, can be very confusing. Having only forks for every possible dish is not as good as choosing between a fork and a spoon, and although scala supports lazy language-level judgment, it defaults to an impatient model. The reason (besides the personal choice of Martin and other language developers) - the interaction between Java and scala - would be a nightmare to write these two worlds in one language. Moreover, during scala, the JVM design was not yet to support such functions, and more or less the executor lazy vals a> became possible only with the introduction of method handles in Java 7 (only two years ago, whereas scala existed for a decade).

+4
source

I will answer my question. So one use case when lazy values ​​are extremely useful is if you want to create an immutable data structure with loops. Which is easy to do without lying, because otherwise you will have to modify the object that is already created. This is not possible if you want your objects to be immutable. Let me use a simple loop implementation as an example.

enter image description here

So in Scala you can implement this as follows

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

Thus you created an invariable cycle. You can check the result by comparing the links.

 scala> node.in res3: Node = Node@2d928643 scala> node.in.in res4: Node = Node@3a5ed7a6 scala> node res5: Node = Node@3a5ed7a6 
+1
source

Source: https://habr.com/ru/post/957997/


All Articles