LARA

Higher-Order Functions

None of the previous techniques works when nested functions are returned as arguments

Consider example:

object Test extends Application {
  def greet(firstLine : String, message : String) = {
    println(firstLine)
    println(message)
  }
 
  def getGreeter(title : String) : (String => Int) = {
    var myLine = "Dear " + title
    def myGreeter(name : String) = {
      println(myLine + " " + name)
      myLine = myLine + "..."
      println("How are you doing today?")
      42
    }
    myGreeter
  }
 
  val (g  : (String => Int)) = getGreeter("Mr")
  val (g1 : (String => Int)) = getGreeter("")
 
  val k = g("Santa")
  val kk = g("Nicolas")
  val k1 = g1("Schmutzli")
}

When getGreeter returns a function, how can this function (g or g1) know what the value of 'myLine' is?

  • 'myLine' value must remain even after 'getGreeter' returns
  • cannot allocate frame for getGreeter on stack, must use heap
  • to invoke 'g' and 'g1' must obtain both code pointer and frame pointer

A pair of code and frame pointer is called closure

  • frame pointer becomes an argument of the function (instead of being created during function invocation)
case class MyGreeterEnv(var myLine : String)
 
def myGreeter(env : MyGreeterEnv, name : String) : Int = {
  println(env.myLine + "..." + name)
  env.myLine = env.myLine + " "
  println("How are you doing today?")
  42
}
 
type myGreeterClosure = (MyGreeterEnv, ((MyGreeterEnv, String) => Int))
 
def getGreeter(title : String) : myGreeterClosure = {
  val defEnv = MyGreeterEnv("Dear " + title)
  (defEnv, myGreeter)
}
 
// the types are specific to this example
val (c,g) : myGreeterClosure = getGreeter("Mr")
val (c1,g1) : myGreeterClosure = getGreeter("")
 
val k = g(c,"Santa")
val kk = g(c,"Nicolas")
val k1 = g1(c1,"Schmutzli")

Note: clients that invoke g,g1 need not know the fields of myGreeterEnv, only the functions myGreeter,getGreeter need to know these fields

  • it means we can store local functions of type (String ⇒ Int) created at different places in the programs

Higher-order function can be encoded as an object with 'apply' method (as in Scala)

  • closure is then an object with one method
  • the receiver ('this') argument plays the role of environment (frame pointer)