Go to the first, previous, next, last section, table of contents.

Using First-Class, Higher-Order, and Anonymous Procedures (Hunk L)

==================================================================
Hunk L starts here:
==================================================================

In this section, we'll play with Scheme's procedures, to illustrate

I'll just briefly demonstrate those ideas for now; later programming examples will show how they're really useful.

First-Class Procedures

Scheme procedures are first-class objects in the language; you refer to a procedure in the same way you refer to any other object, via a pointer. A "procedure name" is really just a variable name, and you can do the same things with "procedure" variables as with any other variable. There's really only one kind of variable in Scheme, and it's type is "pointer to anything."

When we "define a procedure" in Scheme, we're really just defining a variable and giving it an initial value that's (a pointer to) a procedure object.

The procedure defining syntax with parentheses around the procedure name (and argument names) is really just syntactic sugar, i.e., a convenient way of writing something that you could do in another way.

For example,

Scheme>(define (double x)
          (+ x x))
#void

is exactly equivalent to

Scheme>(define double
               (lambda (x)
                  (+ x x))
#void

Try this latter version in your system. Notice that what you're doing is just defining a variable named double and initializing it with the result of the second expression, a lambda expression.

lambda is the real procedure-creating operation. It's a special form, because it lets you define a new procedure rather than calling an existing procedure in the normal way. lambda creates a procedure object and returns a pointer to it.

(The predicate procedure? can be used to tell if an object is a procedure.)

You can call the double procedure created this way in exactly the same way as one created with the sugared procedure-definition syntax.

Scheme>(double 3)
6

Recall how procedure calls really work. When you call a named procedure, e.g., (double 3), the procedure name is really just a reference to a variable. The first position in the procedure call form is just an expression that's evaluated like any other. In this case, we're using the name double as an expression, effectively saying "look up the value of double."

Try this

Scheme>double
#<procedure>

Notice that we didn't put parentheses around double, so we're not calling it--we're fetching the value of the variable double. What you see on your screen may vary, but it's your system's printed representation of a procedure object. Take a look at it, because you'll want to be able to recognize procedure objects in data structures.

(The printed representation may include the name of the procedure; don't be misled by this. Procedures don't really have names--they're just data objects you can have pointers to, as I'll explain shortly. Your system your system may put a name inside the procedure when you use the procedure definition syntax, but it's just an annotation saying what the procedure's original name was--i.e., when it was first defined.)

We can call a procedure in other ways, though--the first subexpression of a procedure call can be any expression we want, as long as it returns a procedure. That expression is evaluated just like the argument expressions--after it and the argument expresssions are evalutated, the resulting procedure is called with those argument values.

Scheme>(define list-holding-double (list double))
#void

Scheme>list-holding-double
(#<procedure>)

Scheme>((car list-holding-double) 5)
10

What we did here was to create a list holding the procedure formerly known as double, and looked at that list. Then we called that procedure by using the expression (car list-holding-double) as its "name."

What this shows is that procedures are really anonymous, that is, a procedure doesn't have a name in a direct sense. There are just expressions we can refer to it by, if those expressions result in pointers to the procedure.

We can create procedures without normal names at all, by just using lambda. Let's create another doubling procedure by just evaluating a lambda expression:

Scheme>(lambda (x) (+ x x))
#<procedure>

The lambda expression just created a procedure and returned a pointer to it, and Scheme displayed it however your system does it. We didn't keep a pointer to the procedure, so we can't call it now. The procedure is gone and the garbage collector will clean it up.

We could try again, creating a procedure and keeping a pointer to it in a named variable. More interestingly, we can just hand the pointer to a procedure call, and call it without ever giving it a name.

Scheme>((lambda (x) (+ x x)) 6)
12

It may not look like it, but this is just a procedure call expression, where the "name" of the procedure is a lambda expression to create the procedure we need, and its argument is 6. Note the nesting of parentheses--this is just like (double 6), except that we give the "definition" of the procedure to call, instead of its name.

Later we'll show why using lambda directly is often much more convenient than having to name all of our procedures. I'll also explain why lambda is the most important special form in Scheme--it is so powerful that most of the special forms can easily be translated into it.

(You might be concerned that creating a procedure and just using it once is very expensive, but it turns out not to be--I'll explain that later, too. For now, don't worry about it.)


Go to the first, previous, next, last section, table of contents.