3

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.

fresskoma
  • 25,481
  • 10
  • 85
  • 128
dublintech
  • 16,815
  • 29
  • 84
  • 115
  • not sure if an exact duplicate, but this question is close: http://stackoverflow.com/questions/6939908/scala-functions-with-no-arguments – Paolo Falabella Jan 20 '13 at 22:07
  • I don't think it's a duplicate, although it is related. This questions focuses explicitly on by-name parameters so such an equivalency (or lack-thereof) should be explicitly stated in an answer. –  Jan 20 '13 at 22:09
  • 1
    (While I'm sure there are plenty of holes in this, I usually view call-by-name as creating an implicit wrapping of the argument passed inside an anonymous function. If such a simple view is taken then `{() => x}` and `{() => x()}` can be viewed as equivalent proxies when `x` is equivalent to `x()`.) –  Jan 20 '13 at 22:11
  • 1
    @pst: This is inadvisable. By-name parameters may be implemented much like an anonymous, zero-argument function, but they're quite distinct. In particular, you cannot get at the "function" representing a by-name actual parameter. _All_ you can do is have the actual parameter expression evaluated (as many times as you like) and that is not done by suffixing a `()` as it would be for a zero-arg function. – Randall Schulz Jan 21 '13 at 15:04

1 Answers1

8

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.

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407