Some questions about difference between call-by-na

2020-02-11 02:38发布

问题:

There are some discussions here about this, but I have some specific questions I wasn't able to find an answer for. So, by call-by-name, I mean =>T type, and by 0-arity function I mean () => T

I understand (I think) the conceptual difference, but probably I am missing something as I still have lots of questions:

  1. Why do we have the concept of =>T at all if we could always use () => T?
  2. Is there any syntax/functional limitations of each? For now I found only that => cannnot be used as a class field. Is this the only limitation?
  3. Is the generated code always the same for both?
  4. Should I always prefer =>T? And why?
  5. Is it correct to call =>T a type? It looks for me like it does not have any type representation in scala.

回答1:

1) It's just more handy to use it, especially inside DSLs:

def printAndGet[T](f: => T) = {
  val res = f
  println(res + " printed") 
  res
}

scala> :paste
// Entering paste mode (ctrl-D to finish)

val k = printAndGet {
  val a = 5
  5 * a 
}

// Exiting paste mode, now interpreting.

25 printed
k: Int = 25

2) => T can only be a parameter of method or function. And actually => T and () => T aren't interchangable:

scala> def aaa(f: => String) = f
aaa: (f: => String)String

scala> val a: Function1[() => String, String] = aaa _
<console>:8: error: type mismatch;
 found   : (=> String) => String
 required: (() => String) => String
       val a: Function1[() => String, String] = aaa _
                                                ^

Thanks to @som-snytt, fоund this one:

scala> object O { def f(i: Int) = i; def f(i: => Int) = i + 1 }
defined object O

scala> O.f(5)
res12: Int = 5

scala> O.f(5: (=> Int))
<console>:1: error: no by-name parameter type allowed here
       O.f(5: (=> Int))
           ^

Even this which should work if it compiles - but it doesn't (scala 2.11.2, 2.11.5 REPL just crashes):

scala> val k: (=> Int) => Int = O.f _
k: (=> Int) => Int = <function1>

scala> k(5) //should be 6
res18: Int = 5 //WTF?

Last one seems like a bug

3) Not exactly, if you want the same, just convert => T into () => T:

scala> def aaa(f: => String) = {f _}
aaa: (f: => String)() => String

Bytecode may also differ. For instance, compiler will more likely inline code from => T without generating lambda for it. So, the key difference is that () => T is actually an object (first class citizen), => T isn't.

4) see 1, but sometimes you may need to ensure that user knows that computation might be delayed - () => T is better then.

5) It's part of a type signature, just look at eta-expansion of:

scala> def aaa(f: => String) = {f}
aaa: (f: => String)String

scala> aaa _ //convert method into a function
res7: (=> String) => String = <function1>

scala> val a: ( => String) =>  String = aaa _
a: (=> String) => String = <function1>

However scala doesn't recognize it as independent type:

scala> val a: Function1[( => String), String] = aaa _
<console>:1: error: no by-name parameter type allowed here
       val a: Function1[( => String), String] = aaa _
                          ^


标签: scala