scalacallbyname

Why call-by-name parameter expect parameter of type Int instead of () => Int


I'm a little bit confused about using call-by-name parameters in Scala. Please help me to understand what is going on here. Consider the following example of using call-by-name parameter:

  def param = {println("Param evaluates"); 40}
  def lazyEval(x: => Int) = {
    println("lazy"); 
    x 
  }
  val s = lazyEval(param + 2)
  // s = 42

I have several question related to each other about this:

  1. lazyEval method expects parameter of type => Int, so why param + 2 operation is legal? Why we can add integer 2 to object with type => Int (im my understanding under the hood it is <function0>) when calling lazyEval method? As IDE hints me, the lazyEval function expects parameter of type Int instead of => Int (what the hell?).

  2. Why after changing callback type from => Int to () => Intcode doesn't compile? Is this 2 types are different? I though the short version ('=> Int') is just a syntactical sugar.

  3. After some playing with code, I finally could change code so it will compile with () => Int. This way is more intuitive for me.

.

  def param = {println("Param evaluates"); 40}
  def lazyEval(x: () => Int) = { // changed type to '() => Int'
    println("lazy"); 
    x() // explicitly calling function using parentheses '()'
  }    
  val s = lazyEval(param _) // adding underscore after method name and removing `+2`

What is differences between this version and first one (with callback => Int type)? Why in this version we can't make addition of integer with value 2 and the param function (I mean thislazyEval(param _ + 2))? And what mean underscore after method name? (I guess it used to pass method itself, not it return value)

Thanks in help


Solution

  • so why param + 2 operation is legal? Why we can add integer 2 to object with type => Int

    We can add 2 to param because it evaluates to Int, you're adding Int with Int. param isn't a function => Int, it is a method, so param + 2 is => Int. ie. an expression that evaluates to Int.

    Why after changing callback type from => Int to () => Intcode doesn't compile? Is this 2 types are different? I though the short version ('=> Int') is just a syntactical sugar.

    => Int and () => Int don't mean the same thing. One is anything that evaluates to Int, and the other a function from Unit to Int. 2 isn't a () => Int, but () => 2 is. Or you could also say 2 is => Int.

    Why in this version we can't make addition of integer with value 2 and the param function (I mean thislazyEval(param _ + 2))? And what mean underscore after method name? (I guess it used to pass method itself, not it return value)

    param is a method, not a function. The underscore in this case lifts param into a function. So param _ is a function () => Int. And that's exactly why we can't add 2 to it, because you can't add 2 to a function. Basically the exact reason why you think (1) shouldn't work.


    In summary:

    def lazyEval(x: => Int) is a method with a parameter x that can be anything that evaluates to Int. That could be any method that returns Int, a concrete value of Int, or some block of code that resolves to Int, etc.

    lazyEval(x: () => Int) is a method with a parameter x that can only be a parameterless function that returns Int. That could mean the method param lifted to a function, or something weird like () => 2. But it must be a function. So simply passing a value like 2 cannot work.