While the idea of producing a function may seem strange at first, it is extremely useful. Before we can discuss the usefulness of the idea, though, we must explore how a function can produce a function. Here are three examples:
(define (f x) first)
(define (g x) f)
(define (h x)
(cond
((empty? x) f)
((cons? x) g)))
The body of f is first, a primitive operation, so
applying f to any argument always evaluates to
first. Similarly, the body of g is f, so
applying g to any argument always evaluates to f.
Finally, depending on what kind of list we supply as an argument to
h, it produces f or g.
None of these examples is useful but each illustrates the basic idea. In the first two cases, the body of the function definition is a function. In the last case, it evaluates to a function. The examples are useless because the results do not contain or refer to the argument. For a function f to produce a function that contains one of f's arguments, f must define a function and return it as the result. That is, f's body must be a local-expression.
Recall that local-expressions group definitions and ask DrScheme to evaluate a single expression in the context of these definitions. They can occur wherever an expression can occur, which means the following definition is legal:
(define (add x)
(local ((define (x-adder y) (+ x y)))
x-adder))
The function add consumes a number; after all, x is added
to y. It then defines the function x-adder with a
local-expression. The body of the local-expression is x-adder, which means the
result of add is x-adder.
To understand add better, let us look at how an application of add to some number evaluates:
(define f (add 5))
= (define f (local ((define (x-adder y) (+ 5 y)))
x-adder))
= (define f (local ((define (x-adder5 y) (+ 5 y)))
x-adder5))
= (define (x-adder5 y) (+ 5 y))
(define f x-adder5)
Designing Abstractions with First-Class Functions
The last step adds the function x-adder5 to the collection of our definitions; the evaluation continues with the body of the local-expression, x-adder5, which is the name of a function and thus a value. Now f is defined and we can use it:
(f 10) = (x-adder5 10) = (+ 5 10) = 15That is, f stands for x-adder5, a function, which adds 5 to its argument.
Using this example, we can write add's contract and a purpose statement:
;; add : number -> (number -> number)
;; to create a function that adds x to its input
(define (add x)
(local ((define (x-adder y) (+ x y)))
x-adder))
The most interesting property of add is that its result
``remembers'' the value of x. For example, every time we use
f, it uses 5, the value of x, when add
was used to define f. This form of ``memory'' is the key to our
simple recipe for defining abstract functions, which we discuss in the next
section.