[previous] [up] [next]     [index]
Next: Designing Abstractions with Functions-as-Values Up: Designing Abstractions with First-Class Previous: Designing Abstractions with First-Class

Functions that Produce Functions

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) 
= 15 
That 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.


[previous] [up] [next]     [index]
Next: Designing Abstractions with Functions-as-Values Up: Designing Abstractions with First-Class Previous: Designing Abstractions with First-Class

PLT