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.