[   ^ to index...   |   <-- previous   |   next -->   ]

Higher-order functions and closures

Critical point: a closure's environment pointer refers to the environment that encloses the point of function definition, not the point of function application. So far, we haven't really exploited this much, but it is very important for higher-order functions.

Functions as parameters

When you pass a function as a parameter, its values do not get rebound:

val a = 15;
fun addA(k) = k + a;
fun doAction(f, a) = f(a);

(* At application of doAction, notice that the a in addA will still
   refer to the a from the global scope---not the parameter a in
   doAction.  The result of this expression is 18, not 6. *)
val aPlus3 = doAction(addA, 3);

Exercise: Can you draw the environment at each step of the evaluation of the code above?

Functions as return values

Consider the function makeAddX(x) which we discussed last week:

fun makeAddX(x) = fn y => x + y;

val add5 = makeAddX 5;

val eleven = add5 6;

makeAddX returns a function that uses a value from its enclosing scope. The activation environment will persist until it is no longer reachable. In other words, activations are allocated on the heap and may have indeterminate lifetimes.

Exercise: Can you draw the environment at each step of the evaluation of the above lines?

Anonymous functions and let-expressions

You may have noticed that function application and let-expressions both produce a new environment with bindings. Perhaps this doesn't set off your non-orthogonality detector, but it should: why have two mechanisms when you can have one?

It turns out that all let expressions can be rewritten using only anonymous functions! Can you see how? Try to rewrite the following using only anonymous functions and function application:

let
    val x = 3;
    val y = 4;
in
    x + y
end;

Keunwoo Lee
Last modified: Wed May 2 21:21:27 PDT 2001