Using apply ("()") on a function passed in by-name parameter: isn't the evaluation forced?
I have a function:
def nanoTime() = {
println("Getting nano time...")
System.nanoTime // returns nanoTime
}
and another function that takes a function
def printTime(time: => Long) = { // indicates a by-name parameter
println(">> delayed()")
println("Param: " + time)
time // returns time
}
Now here's the thing. When I do this:
scala> printTime(nanoTime())
>> delayed()
Getting nano time...
Param: 546632085606127
Getting nano time...
res11: Long = 546632086131624
I get the same results as in the case:
scala> printTime(nanoTime)
>> delayed()
Getting nano time...
Param: 546622367510997
Getting nano time...
res10: Long = 546622368149903
There is no difference between:
scala> printTime(nanoTime())
and
scala> printTime(nanoTime)
So there is no difference between passing a function name and passing a function name followed by a (). Is this always the case, or what is special about this business?
Thank.
source to share
Scala has the concept of parameter lists where a method can take more than one. However, it also lets you exclude empty terminal parameter lists as a convenience. So
f
f()
f()()
everything can be the same - you don't know until you look at f
. Setting the by-name parameter is to defer code execution. Now, formally, if we have
def f0: String = "salmon"
def f1(): String = "herring"
def f2()(): String = "halibut"
then you would expect to f0
match the by-name parameter and the rest won't if converted to a function. In particular, you expect
f0 <==> => String
f1 <==> () => String
f2 <==> () => () => String
when converting. See what actually happens when requested via f _
:
scala> f0 _
res4: () => String = <function0>
scala> f1 _
res5: () => String = <function0>
scala> f2 _
res6: () => () => String = <function0>
Good; f0
is actually converted to a function with one empty parameter block instead of zero (this is what the by-name parameter looks like). So it turns out that your by-name parameter does not convert your method to a function at all - the type signatures don't match!
So it is like this:
// I need a code block that returns a long
nanoTime // Wait, there is no nanoTime exactly
nanoTime() // Aha, that works! Must have meant that
: => { nanoTime() } // There, nicely packaged.
The reason you don't see the difference is because Long
the by-name parameter already fills in the missing one to return ()
, but then wraps it all up into a block of code to execute later.
(Note also that the by-name parameters are actually just Function0
under the hood - that is, x: => A
really x: () => A
- and the "zero parameter ones" block is just compiler fiction., All parameter blocks are fictional compiler - the JVM only knows about one parameter list . And it is this block-less fiction, combined with who-cares-about-empty-parens fantasy, that leads to the observed behavior.)
If you request a function from an empty parameter block, it goes like this:
def printF(f: () => String) = println(f())
scala> printF(f0)
<console>:23: error: type mismatch;
found : String
required: () => String
printF(f0)
^
scala> printF(f1)
herring
scala> printF(f2)
<console>:23: error: type mismatch;
found : () => String
required: String
printF(f2)
scala> printF(f2())
halibut
where now parens matter because the compiler is trying to match the method signature with the function signature. The special cases of the roll-call parameter situation no longer apply.
source to share