Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
93 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Closures

Lisp allows you to create new functions at runtime.  For example, the following function takes a number and returns a new function which increments by that number:

(define (make-incrementor delta)
  (lambda (x)
    (+ x delta)))
Example 3.

The lambda creates an anonymous function (a function with no name).  To use such a function at a later date, we can give it a name using define.   The following session shows how we do this.  The > is a prompt, and values returned by the computer are in bold.

> (define inc-by-2 (make-incrementor 2))
#<procedure inc-by-2>

> (inc-by-2 3)
5
Example 4.

There is a more direct way of doing this, if we only wish to use the function once:

> ((make-incrementor 2) 3)
5
Example 5.

Here's why the above example works:

Each expression in Lisp is a list enclosed in parentheses.  When this list is evaluated by Lisp, each element of the list is evaluated recursively.  The first element of the list is assumed to be a function, and is applied to the rest of the list as the arguments.  In the above example, the first element of the list is itself a list, so it is evaluated and returns a function, which is then applied to the argument 3.

What is a closure, which we mentioned above?  Notice that in Example 3, the definition of the anonymous function depends on the value of delta, which is passed in at runtime.  What is returned by make-incrementor is a closure, which is a function along with the bindings it has captured from the environment at the time of its creation.  This is what allows a different function to be returned for different values of delta.

Macros

Macros are very useful in C, and are used very frequently because they increase the expressive power of the language.  But despite their usefulness, every C programmer has at one time or other wanted to do something using macros that was not possible, because what the C macros do is simple string substitution.

Lisp macros go way beyond that.  Instead of using string substitution, they operate on expressions of code, and allow the use of all the power of the Lisp language itself in defining the macros.

Say we want to add a conditional unless to Lisp (actually, Common Lisp already has this).  We can do it using a macro:

> (define-macro (unless test body else)
    `(if (not ,test)
       ,body
       ,else))
#<macro unless>

> (unless (> 2 0)
    (/ 1 0)
    5)
5

> (macro-expand '(unless (> 2 0)
                   (/ 1 0)
                   5))
(if (not (> 2 0)
  (/ 1 0)
  5)
Example 6.

Don't worry about the syntactic details of the macro definition.  What it does should be clear to experienced programmers.  In effect, we can define a template for some code and fill it with values which are passed in at runtime.  In this case, these values are the test condition, the code to execute if the test is true, and the code to execute otherwise.

We can in fact do much more that simply have a static template which we fill with values.  Remember that all expressions in Lisp are enclosed in parentheses.  In Lisp, things in parentheses are considered lists, which are data, just like any other data type in the language (vectors, strings, characters, etc.).  What this means is very significant: the code is represented as data.  This means that we can use the language itself to operate on lists (take them apart, build new lists) in order to generate the templates for macros.  We can write code which generates code.  This is why Lisp has all those parentheses.  It is the complete uniformness of the language syntax which allows macros to be so powerful in Lisp.  In effect, the code you write is already in a parse tree format, ready for processing by the language itself.

So how is the above macro different from a function?  Why can't a function definition using define do the same thing?

Consider this: in Lisp, as in C, the code in the false branch of a conditional should not be executed.  In the example above, this code is (/ 1 0), which would cause a division-by-zero error if it is executed.  But remember than when Lisp evaluates an expression, it evaluates every element of the expression, so it would try to evaluate all the arguments of the unless expression if it were a function. But when Lisp encounters a macro, it passes the arguments to the macro without evaluating them!  The entire expression returned by the macro is evaluated.  In this case, that expression is an if expression, which means that the offending code is never evaluated due to the evaluation rules of if.

What we have done here is to extend our language with a new conditional construct.  The important thing to realize is that this new conditional works exactly like the ones built into the language.  This is something which is not possible in most other languages.  This is why Lisp is referred to as a programmable programming language.


Contents
  Page 1
  Page 2

  Printable version
  Discuss this article