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.

+3


source to share


1 answer


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.

+8


source







All Articles