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

Anonymous functions (a.k.a. "lambdas" or "closures")

If you had to bind every number to a name ("val zero = 0") before using it, you would probably get annoyed. High-level languages typically provide a way to define anonymous functions. In ML, you use fn to construct a function expression:

- fn x => x;           (* identity function *)
val it = fn : 'a -> 'a
- fn x => x + 5;       (* adds 5 to its argument *)
val it = fn : int -> int
- (fn x => x + 5) 4;   (* constructs and immediately applies function *)
val it = 9 : int

Immediately applying an anonymous function isn't too interesting. Here's a more typical use of anonymous functions, as parameters:

datatype ComplexNum = Complex of real * real;

val myNums = [Complex(0.0, 1.0), Complex(1.2, 0.6), Complex(2.5, 3.2)];

val sortedByReals = 
    qsort(fn (Complex(r1,_), Complex(r2,_)) => r1 > r2, myNums);

val sortedByImag =
    qsort(fn (Complex(_,i1), Complex(_,i2)) => i1 > i2, myNums);

For historical reasons, anonymous functions are often called "lambdas". This is because of the language Lisp, which was based on a formal mathematical description of computation called the "lambda calculus" (invented by Alonzo Church). In Lisp, you define functions by using the lambda keyword (we'll see more of this when we study Scheme):

(lambda (x) (+ x 5))  ; Equivalent to ML's (fn x => x + 5)

Returning functions

Since functions are values, they can be returned as well:

(* Makes a single-argument function that adds x to its argument *)
fun makeAddX(x) = fn y => x + y;

val add5 = makeAddX(5);   (* A function that adds 5 to any int *)
val foo = add5 4;         (* foo = 9. *)

Notice that add5 "remembers" that x was bound to 5 in the fn expression. Anonymous functions contain a pointer to the environment in which they were "captured"; that environment is called a closure. (So why is the environment called a closure? Long story.)

What use is all this, you ask? Well, here's one slightly less silly use:

datatype complexPart = RealPart | ImagPart;

fun sortBy(RealPart) = (fn (Complex(r1,_), Complex(r2,_)) => r1 > r2)
  | sortBy(ImagPart) = (fn (Complex(_,i1), Complex(_,i2)) => i1 > i2);

qsort(sortBy(RealPart), myNums);

Keunwoo Lee
Last modified: Wed Apr 25 22:40:09 PDT 2001