Reputation: 17785
I have a function:
def nanoTime() = {
println("Getting nano time...")
System.nanoTime // returns nanoTime
}
and another function, which 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:
scala> printTime(nanoTime())
>> delayed()
Getting nano time...
Param: 546632085606127
Getting nano time...
res11: Long = 546632086131624
I get the same results for when I do:
scala> printTime(nanoTime)
>> delayed()
Getting nano time...
Param: 546622367510997
Getting nano time...
res10: Long = 546622368149903
There is no difference betweeen:
scala> printTime(nanoTime())
and
scala> printTime(nanoTime)
So there is no difference between passing the function name and passing the function name followed by (). Is this this always the case, or what is so special about this casE?
Thanks.
Upvotes: 3
Views: 342
Reputation: 167871
Scala has the concept of parameter lists where a method may take more than one. However, it also, as a convenience, allows terminal empty parameter lists to be omitted. So
f
f()
f()()
might all be the same thing--you don't know until you look at f
. The job of a by-name parameter is to delay execution of a code block. Now, formally if we have
def f0: String = "salmon"
def f1(): String = "herring"
def f2()(): String = "halibut"
then you would expect f0
to match a by-name parameter and the others not to, if converted to a function. Specifically, you would expect
f0 <==> => String
f1 <==> () => String
f2 <==> () => () => String
when converted. Let's see what actually happens when requesting via f _
:
scala> f0 _
res4: () => String = <function0>
scala> f1 _
res5: () => String = <function0>
scala> f2 _
res6: () => () => String = <function0>
Oh well; f0
actually converts into a function with one empty parameter block instead of zero (which is what a by-name parameter looks like). So it turns out that your by-name parameter is not converting your method into a function at all--the type signatures won't match!
So instead, it reasons like so:
// 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 see no difference is that in order to return a Long
, the by-name parameter is already filling in the missing ()
, but then wrapping the whole thing in a code block to execute later.
(Note also that by-name parameters are actually just Function0
under the hood--that is, x: => A
is really x: () => A
--and the "zero parameter blocks" thing is just a compiler fiction. Actually, all parameter blocks are a compiler fiction--the JVM only knows about a single parameter list. And it is this no-blocks fiction, coupled with the who-cares-about-empty-parens fiction, that results in the observed behavior.)
If you request an function from an empty parameter block, then things work like so:
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 do matter because the compiler is trying to match the method signature to the function signature. The special cases of the by-name parameter situation no longer apply.
Upvotes: 8