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)