Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Scala, when would be a good time to use lazily evaluated parameter rather than to use a function as a parameter?

def getStr(): String = {
  println("getStr is running")
  "str"
}

def lazyHello(para: => String) = {
  println("lazy hello is runing")
  println(para)
}

def notLazyHello(para: String) = {
  println("not lazy hello is runing")
  println(para)
}

def anoyHello(para: () => String) = {
  println("anoy hello is runing")
  println(para())
}

notLazyHello(getStr)
lazyHello(getStr)
anoyHello(getStr)

got this result:

scala> notLazyHello(getStr)
getStr is running
not lazy hello is runing
str

scala>     lazyHello(getStr)
lazy hello is runing
getStr is running
str

scala>     anoyHello(getStr)
anoy hello is runing
getStr is running
str

seems like lazyHello and anoyHello perform the same.

So, in Scala, when would be a good time to use lazily evaluated parameter rather than to use a function as a parameter?

like image 705
Cui Pengfei 崔鹏飞 Avatar asked Nov 19 '13 12:11

Cui Pengfei 崔鹏飞


2 Answers

Your observation is correct. lazyHello and anoyHello are in fact the same. This is because para: => String is shorthand for para: () => String.

Another way to look at this:

() => String is a function that takes no parameters and returns a string.

=> String is something that evaluates to a string, without taking parameters. So essentially call-by-name is a function without an input parameter.

like image 97
Akos Krivachy Avatar answered Nov 15 '22 07:11

Akos Krivachy


Basically there's no technical difference between lazy evaluated parameter and Function0 parameter. There are pro's and cons of both the implementation.

Function0 parameter is definitely verbose. So if you change them to lazy parameter, code will become much readable.

Here is a bug prone situation of lazy evaluation.

Consider following code

def inner = println("inner called")

def execute(f: => Unit) = {
  println("executing")
  f
}

def redirect(f: => Unit) = {
  println("redirecting")
  execute(f)
}

redirect(inner) // will print - 
                // redirecting
                // executing
                // inner called

Now think for some reason you forget to put => in the execute function. Code will compile correctly, but the behavior will be different and might introduce bugs.

def execute(f: Unit) = {
  println("executing")
  f
} 

def redirect(f: => Unit) = {
  println("redirecting")
  execute(f)
}

redirect(inner) // will print - 
                // redirecting
                // inner called
                // executing

But if you use Function0, the code will not compile. So, the Function0 parameters are less bug prone.

def inner() = println("inner called")

def execute(f:() => Unit) = {
  println("executing")
  f()
}

def redirect(f:() => Unit) = {
  println("redirecting")
  execute(f)
}

redirect(inner) // will print - 
                // redirecting
                // executing
                // inner called

=========================================

def inner() = println("inner called")

def execute(f:Unit) = {
  println("executing")
  f
}

def redirect(f:() => Unit) = {
  println("redirecting")
  execute(f)               // Will not compile.
}

Furthermore, You can clearly see when that value is evaluated.

Anyway if you are so sure what you do, you can use any one of them. Decision is yous.

like image 20
tiran Avatar answered Nov 15 '22 09:11

tiran