Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does by-name arguments work in higher-order functions?

Tags:

scala

I'm trying to understand what by-name type annotations mean in the context of higher-order functions. Here's an example:

object Test { 
  def apply[A, B](f: (=> A) => B, x: => A): B = f(x) 
  def const[A](ignored: A): Int = 1
  val res: Int = apply(const, ???)
}

const is strict in its argument (i.e. it lacks a => annotation), so why isn't it forcing its argument (which is ??? in this case) and raises an exception?

Is there a paper describing the semantics here?

I'm looking for an authoritative answer here.

like image 837
tibbe Avatar asked Apr 18 '13 23:04

tibbe


1 Answers

The argument f in your apply function is a Funcion1 which takes a call-by-name parameter of type A and returns a B. Therefore, f(x) will not evaluate the call-by-name parameter x, but pass its reference directly to f.

It is helpful to understand res as follows:

def res[C]: Int = apply[C, Int](const, ???)

where in your example C would be an unspecific type. Now what is the type parameter inferred for const in this line? It is => C. Unfortunately you cannot type that parameter:

def res[C]: Int = apply[C, Int](const[=> C], ???)  // illegal syntax

But you can verify what is going on:

def res[C]: Int = apply[C, Int](const[Nothing], ???)

giving you

<console>:10: error: type mismatch;
 found   : Nothing => Int
 required: => C => Int
         def res[C]: Int = apply[C, Int](const[Nothing], ???)
                                              ^

This type appears inside const as a Function0[Int] (so Scala implicitly treats call-by-name or "thunk" arguments as a function with no arguments). Again you can verify this:

def const[A](ignored: A): Int = if (ignored.isInstanceOf[Function0[_]]) 1 else 0

Now Test.res will give you 1 (meaning that ignored is indeed a Function0).


So to answer the question a different way, const has an eager argument of type A, but that doesn't matter here because A becomes a function in your example, and you never apply that function, therefore ??? is never executed.


There is some debate as to why there is both a "thunk" or parentheses-less function and an empty-paren function (Function0) in Scala.

like image 97
0__ Avatar answered Sep 28 '22 11:09

0__