4

In scala, there is some call-by-name parameters:

def hello(who: => String) = println("hello, " + who)

What's the type of the parameter who?

It shows the function on scala REPL as:

hello: (who: => String)Unit

Is the type still => String? Is there any name for it? Or some documentation to describe the type?

Further questions raised by answer

Question 1

(When reading the spec of §3.3.1 (MethodTypes))

Method type is the type of a method, say I defined a method hello:

def hello: String = "abc"

The type of the it can be written as: => String, right? Although you can see the REPL response is:

scala> def hello:String = "abc"
hello: String

If I define a method which has parameters:

def goodname(name: String): String = name + "!"

What's the type of the method? It should be similar to String => String, but not. Because it's a method type, and String => String is a function type.

Question 2

(When reading the spec of §3.3.1 (MethodTypes))

I can understand this as:

def goodname(name: String): String = name + "!"
def print(f: String => String) = println(f("abc"))
print(goodname)

When I call print(goodname), the type of goodname will be converted to the function type String => String, right?

But for paramless method:

def hello: String = "abc"

What function type can it be converted? I tried:

def print(f: () => String) = println(f())

But this can't be compiled:

print(hello)

The error is:

error: type mismatch; found : String required: () => String

Could you give me an example that works?

Question 3

(When reading the spec of §6.26.2 (MethodConversions))

This Evaluation conversion is only happened when the type is not applied to argument. So, for code:

def myname:String = "abc"
def print(name: => String) = println(name)
print(myname)

My question is, when I call print(myname), is there conversion(I mean Evaluation conversion) happened? I guess, since the type of myname is just => String, so it can be passed to print directly.

If the print method has changed:

def myname:String = "abc"
def print(name: String) = println(name)
print(myname)

Here the Evaluation conversion is definitely happened, right?(From => String to String)

Community
  • 1
  • 1
Freewind
  • 177,284
  • 143
  • 381
  • 649
  • It seems the type is not `()=>String`, since `hello(() => "Freewind")` can't be compiled – Freewind Aug 20 '14 at 16:29
  • In your examples, what is the type of `myname`? – Sean Vieira Aug 20 '14 at 18:04
  • @SeanVieira, fixed and improved – Freewind Aug 21 '14 at 00:34
  • RE: Question 1 - methods are not values (§3.3) so they have no type as such. They are *converted* to values by wrapping the call to them in a `FunctionN` where the `apply` method calls the underlying method. So when you pass `goodname` to `print` you are passing an instance of `Function1[String, String]` to `print` which *calls* `goodname`: `print(new Function1[String, String] { def apply(v: String) = goodname(v) })` – Sean Vieira Aug 21 '14 at 01:28
  • RE: Question 2 - the parameterless method will be converted to a `Function0[T]`. The reason you are having trouble passing `hello` to `print` is because the compiler is *calling* `hello` (because such methods "are re-evaluated *each time the parameterless method name is referenced*"). You have to explicitly tell the compiler to defer the evaluation with `_`; `print(hello _)` rather than `print(hello)`. – Sean Vieira Aug 21 '14 at 01:32
  • RE: Question 3 - in your first example the evaluation conversion happens when `name` is referenced in the expression `println(name)`. In your second example, yes, you are correct `myname` is evaluated at the call site. `print(myname)` is `print(myname())` which is `print("abc")` – Sean Vieira Aug 21 '14 at 01:37

1 Answers1

7

Quoting from §4.6.1 of the spec:

The type of such a parameter is then the parameterless method type => T.

So the type of a call-by-name param is (approximately) () => T (or Function0[T] if you prefer). If you :javap a method that accepts a call-by-name parameter you will see that the compiled code accepts a param of the type scala.Function0<java.lang.Object>.

An example of the approximation

The translation of:

def callByName[T](f: => T) = f

callByName { /* magic */
    1 + 1
/* ends here */ }

is effectively:

def callByName[T](f: Function0[T]) = f.apply()

callByName(new Function0[Int] {
  def apply() = { /* magic */
    1 + 1
  /* ends here */ }
})

Doubts surrounding the approximation

You may be tempted to try passing a () => T to your method. Try callByName(() => 12); why does it not compile? (Hint, consider the expansion at the call site). (Hover on the following blank to see the answer):

The reason callByName(() => 12) does not compile is because the expansion is treated as: callByName(new Function0[() => Int] { def apply() = () => 12 }) That is, rather than passing in a Function0 which returns an Int you are passing in a Function0 which returns a Function0 which returns an Int.

What => T actually is

=> T is actually a method type, not an object. So everything that goes before is an approximation of what the compiler does and can change at any time. Quoting from §3.3:

The types explained in the following do not denote sets of values, nor do they appear explicitly in programs. They are introduced in this report as the internal types of defined identifiers.

So what are method types? Quoting from §3.3.1 (MethodTypes):

A special case are types of methods without any parameters. They are written here => T. Parameterless methods name expressions that are re-evaluated each time the parameterless method name is referenced.

Method types do not exist as types of values. If a method name is used as a value, its type is implicitly converted to a corresponding function type (§6.26).

And §6.26.3 (MethodConversions) states:

The following four implicit conversions can be applied to methods which are not applied to some argument list.

Evaluation. A parameterless method m of type => T is always converted to type T by evaluating the expression to which m is bound.

So the proper translation of the type => T is always:

def random$name$here: T

Example

Here's an example class to play around with:

class TestParamless {
  def paramless: Int = 1
  def callByName(f: => Int) = f
  def example: Int = callByName(paramless)
}

Try new TestParamless().example and also, :javap TestParamless (in the scala REPL).

Community
  • 1
  • 1
Sean Vieira
  • 140,251
  • 31
  • 286
  • 277
  • 1
    @Freewind, yes, it is. – Gabriele Petronella Aug 20 '14 at 16:05
  • Ok. I want to follow up on this. If it is both `Function0`, then why have different syntax for both cases? `def callByName[T](f: () => T) = f()` requires `()` after `f` and also `callByName{ 1+ 1}` would give a syntax error. You would need `() => 1 + 1`. – Kigyo Aug 20 '14 at 16:22
  • Why `hello(() => "Freewind")` can't be compiled? Seems the type of `=> String` is not same to `() => String` – Freewind Aug 20 '14 at 16:25
  • `() => T` is a function from Unit to T while `=> T` is a call by name and has type T, to me they look different. – Ende Neu Aug 20 '14 at 16:30
  • The reason for the different syntax seems to be to allow easier "blocks" for language extensions. So rather than forcing anyone who wants to log anything (for example) to use: `logger.warn(() => s"Problem with ${potentially.expensiveCallHere()}")` the API designer can tell the compiler, "I want to defer the work passed into me until I explicitly invoke it". Call-by-name is effectively a macro whereas taking a `() => T` makes everything explicit. – Sean Vieira Aug 20 '14 at 16:34
  • `def callByNameR[T](f: => T) = (f _).getClass()` results in `res5: Class[_ <:> Int] = class $anonfun$1` when invoked: `callByNameR { 1 + 1 }` – Sean Vieira Aug 20 '14 at 16:35
  • The issue with `hello(() => "Data")` is that the underlying type of the `Function0` is actually `Function0[() => String]` rather than `Function0[String]` (which of course, does not match the type that `hello` expects. Remember the expansion *at the call site* :-) – Sean Vieira Aug 20 '14 at 16:36
  • 1
    I've added a couple more bits of detail - I'll try to re-work this later to make it clearer. – Sean Vieira Aug 20 '14 at 17:10
  • @SeanVieira, I will add something to your answer directly since the comment is too long – Freewind Aug 20 '14 at 17:38
  • @Freewind - I moved the questions to your question and I'll see which of them I can answer here :-) – Sean Vieira Aug 20 '14 at 18:02
  • @SeanVieira, a new question related some point of answer: http://stackoverflow.com/questions/25473637/why-def-hellotf-t-f-hello-12-is-compilable-but-def-hellof – Freewind Aug 24 '14 at 16:09