From: Dobes Vandermeer
Subject: Implementing Closures
Date: 
Message-ID: <37D37179.1A325362@mindless.com>
I am trying to find an efficient and elegant way of implementing
enclosures.

Closures, I understand, are created when a (function (lambda .... ) )
type form is encountered, and inside of the lambda, a variable in the
parent environment is referenced. 

To illustrate:

(let ((foo
        (let (x y z)
           
           (setq x 10)
           
           (setq y #'(lambda () (* x 2)))
           
           (setq z #'(lambda (v) (setq x v)))
           
           (print (funcall y))    ; ==> 20
           (print (funcall z 3))  ; ==> 3
           (print x)              ; ==> 3
           (print (funcall y))    ; ==> 6
           y
           )))
   (funcall foo) ; ==> Return 6
   )

Here we see that the setq form in "z" (sorry about the short names)
changes the value of x both for the parent context and for "y".  Also,
this variable is persistent, holding past the lifetime of the variables
that were "stolen", and even after the expiration of the let that
defined the variables the enclosed variables are shared among the lambda
functions that use them, such that if I were to return both "y" and "z"
functions, I could call them and they would modify each others' version
of "x".

If you have the answer to my problem, you probably already knew all
that.  In fact, if I am wrong please correct me (especially if it
simplifies the problem).

Some broken solutions come easily to mind:

1. The closures store a pointer or a reference up the stack to the
parent context's variable; this solves the problem of sharing the
variable between closures and the parent context, but is broken because
when the parent context is gone, the variables would not be valid.

2. The closures can create a special object to share that holds a single
value; this extends teh lifetime beyond the parent context's, and allows
sharing between the closures.  Unfortunately, it does not let the
closures modify the variable in the parent context's space.

3. If the parent searches its source before compiling, it might be able
to predict which variables will be used by lambdas, and use a
reference-type variable itself (as in 2.); This would fail if the
closures were defined in a macro, or something else that was expanded
during the course of the function.  Also, this is already increasing the
complexity beyon what I would consider ideal, so its not the direction I
really want to go in.

Regardless, if anyone has a good idea for solving this, I would
appreciate any kind of information.

Direct any large attachments, flame mail, or mail obviously not
interesting to the group directly to me at ············@mindless.com

Thanks
Dobes

From: Barry Margolin
Subject: Re: Implementing Closures
Date: 
Message-ID: <wGKA3.820$m84.21462@burlma1-snr2>
In article <·················@mindless.com>,
Dobes Vandermeer  <·····@mindless.com> wrote:
>3. If the parent searches its source before compiling, it might be able
>to predict which variables will be used by lambdas, and use a
>reference-type variable itself (as in 2.); This would fail if the
>closures were defined in a macro, or something else that was expanded
>during the course of the function.  Also, this is already increasing the
>complexity beyon what I would consider ideal, so its not the direction I
>really want to go in.

I think this is the typical implementation.  Macros don't cause problems,
because the analysis takes place *after* all the macros have been expanded.

This doesn't really add significant complexity.  The compiler already has
to examine the function and determine how to generate code for each
variable reference.

You should be able to find more information about this by reading
proceedings of early Lisp & Functional Programming conferences, as well as
the Scheme papers ("Lambda: the Ultimate XXX").

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Rob Warnock
Subject: Re: Implementing Closures
Date: 
Message-ID: <7qvt82$14tv9@fido.engr.sgi.com>
Dobes Vandermeer  <·····@mindless.com> wrote:
+---------------
| trying to find an efficient and elegant way of implementing enclosures
| ... Some broken solutions come easily to mind:
| 1. The closures store a pointer or a reference up the stack to the
| parent context's variable; this solves the problem of sharing the
| variable between closures and the parent context, but is broken because
| when the parent context is gone, the variables would not be valid.
+---------------

Actually, this is not "broken", and works quite well as long as
the parent's context is *not* deallocated explicitly, but rather is
garbage-collected, in which case it will live just exactly as long
as it needs to (i.e., as long as there are any live references to it).

Start by thinking about *all* bound variables being heap-allocated
(and garbage-collected), and only *then* (after you see how that solves
your problem) consider possible ways to optimize it (e.g., perhaps by
stack-allocating variables that are never referenced by closures).

Read "Lisp In Small Pieces", by Christian Queinnec [Cambridge University
Press] (originally in French as "Les Langages Lisp", InterEditions, 1994),
which discusses such issues in great variety & depth...


-Rob

-----
Rob Warnock, 8L-846		····@sgi.com
Applied Networking		http://reality.sgi.com/rpw3/
Silicon Graphics, Inc.		Phone: 650-933-1673
1600 Amphitheatre Pkwy.		FAX: 650-933-0511
Mountain View, CA  94043	PP-ASEL-IA
From: Fernando Mato Mira
Subject: Re: Implementing Closures
Date: 
Message-ID: <37D543A7.4292F196@iname.com>
Rob Warnock wrote:

> Read "Lisp In Small Pieces", by Christian Queinnec [Cambridge University
> Press] (originally in French as "Les Langages Lisp", InterEditions, 1994),
> which discusses such issues in great variety & depth...

I never read this one because I normally don't like reading translated books,
and computer literature in French can be annoying. But I assume CQ's work must
be excellent. Anyone tried both?

Thanks
From: Rob Warnock
Subject: Re: Implementing Closures
Date: 
Message-ID: <7r5gi0$28qro@fido.engr.sgi.com>
Fernando Mato Mira  <········@iname.com> wrote:
+---------------
| Rob Warnock wrote:
| > Read "Lisp In Small Pieces", by Christian Queinnec [Cambridge University
| > Press] (originally in French as "Les Langages Lisp", InterEditions, 1994),
| > which discusses such issues in great variety & depth...
| 
| I never read this one because I normally don't like reading translated books,
| and computer literature in French can be annoying. But I assume CQ's work
| must be excellent.
+---------------

Even in translation, yes!!  I should mention that the translation to
English was *extremely* good, with only the very occasional odd word
or phrasing.

+---------------
| Anyone tried both?
+---------------

(Sorry, not me.)


-Rob

-----
Rob Warnock, 8L-846		····@sgi.com
Applied Networking		http://reality.sgi.com/rpw3/
Silicon Graphics, Inc.		Phone: 650-933-1673
1600 Amphitheatre Pkwy.		FAX: 650-933-0511
Mountain View, CA  94043	PP-ASEL-IA
From: Fernando Mato Mira
Subject: Re: Implementing Closures
Date: 
Message-ID: <37E25439.53B239E@iname.com>
Rob Warnock wrote:

> Fernando Mato Mira  <········@iname.com> wrote:
> +---------------
> | Rob Warnock wrote:
> | > Read "Lisp In Small Pieces", by Christian Queinnec [Cambridge University
> | > Press] (originally in French as "Les Langages Lisp", InterEditions, 1994),
> | > which discusses such issues in great variety & depth...
> |
> | I never read this one because I normally don't like reading translated books,
> | and computer literature in French can be annoying. But I assume CQ's work
> | must be excellent.
> +---------------
>
> Even in translation, yes!!  I should mention that the translation to
> English was *extremely* good, with only the very occasional odd word
> or phrasing.

I was wondering if the translation is maybe better than the original in this
particular case.
From: Howard R. Stearns
Subject: Re: Implementing Closures
Date: 
Message-ID: <37D9420D.C1AE5F82@elwood.com>
There are book references at
http://www.elwood.com/alu/table/books.htm#impl.  Also, section 2.2 (and
particularly 2.2.2) of
http://www.elwood.com/eclipse/papers/lugm98/lisp-c.html describes how
this is done in Eclipse.

Here's a shortcut.

First, let's think about a very C-like implementation, where things are
compiled and there are C variables, a C "stack", a C "heap", and
pointers.  A function (any function) is represented as some piece of
type-tagged structure that contains a pointer to a function (i.e., the C
function implementing the execution of the closure), and a pointer to an
array of "bindings".  (Perhaps the array of bindings is a separate
structure, or perhaps all closures are a single piece of data of
arbitrary length, depending on how many bindings it needs.  It doesn't
matter for this discussion, but let's say they are spearate peices, just
to keep things explicit.)

If the Lisp closure has indefinate extent (the default), then both the
closure structure and the array of bindings must both be heap
allocated.  If the closure has dynamic extent, then the closure
structure and the array of bindings can be stack allocated.

Separate from the extent of the closure is the extend of the variables
being closed over.  If the variable has dynamic extent (not the
default), then the "binding" stored in the array can just be the address
of the C stack-allocated variable in the surrounding function.  

However, if the varible has indefinite extent, then a separate binding
object must be heap allocated.  The array stores the address of the
binding, we read and write to the binding, not to some local C variable.

This is the basic theme.  One major variation has to do with these
"arrays of bindings".  In the simple model above both heap-allocated
binding structures, and C stack-variable pointers are shared, but the
"array of bindings" itself is not shared between different closures.
Depending on whether you are interpreting or compiling, it may be
convenient to have some tree structure to represent environments, and
the "arrays of bindings" are built out of pieces of that structure. 
This allows sharing of environment structure between different closures
over the same data. 

Dobes Vandermeer wrote:
> 
> I am trying to find an efficient and elegant way of implementing
> enclosures.
> 
> Closures, I understand, are created when a (function (lambda .... ) )
> type form is encountered, and inside of the lambda, a variable in the
> parent environment is referenced.
> 
> To illustrate:
> 
> (let ((foo
>         (let (x y z)
> 
>            (setq x 10)
> 
>            (setq y #'(lambda () (* x 2)))
> 
>            (setq z #'(lambda (v) (setq x v)))
> 
>            (print (funcall y))    ; ==> 20
>            (print (funcall z 3))  ; ==> 3
>            (print x)              ; ==> 3
>            (print (funcall y))    ; ==> 6
>            y
>            )))
>    (funcall foo) ; ==> Return 6
>    )
> 
> Here we see that the setq form in "z" (sorry about the short names)
> changes the value of x both for the parent context and for "y".  Also,
> this variable is persistent, holding past the lifetime of the variables
> that were "stolen", and even after the expiration of the let that
> defined the variables the enclosed variables are shared among the lambda
> functions that use them, such that if I were to return both "y" and "z"
> functions, I could call them and they would modify each others' version
> of "x".
> 
> If you have the answer to my problem, you probably already knew all
> that.  In fact, if I am wrong please correct me (especially if it
> simplifies the problem).
> 
> Some broken solutions come easily to mind:
> 
> 1. The closures store a pointer or a reference up the stack to the
> parent context's variable; this solves the problem of sharing the
> variable between closures and the parent context, but is broken because
> when the parent context is gone, the variables would not be valid.
> 
> 2. The closures can create a special object to share that holds a single
> value; this extends teh lifetime beyond the parent context's, and allows
> sharing between the closures.  Unfortunately, it does not let the
> closures modify the variable in the parent context's space.
> 
> 3. If the parent searches its source before compiling, it might be able
> to predict which variables will be used by lambdas, and use a
> reference-type variable itself (as in 2.); This would fail if the
> closures were defined in a macro, or something else that was expanded
> during the course of the function.  Also, this is already increasing the
> complexity beyon what I would consider ideal, so its not the direction I
> really want to go in.
> 
> Regardless, if anyone has a good idea for solving this, I would
> appreciate any kind of information.
> 
> Direct any large attachments, flame mail, or mail obviously not
> interesting to the group directly to me at ············@mindless.com
> 
> Thanks
> Dobes
From: Dobes Vandermeer
Subject: Re: Implementing Closures
Date: 
Message-ID: <37DA1973.55EBBB26@mindless.com>
"Howard R. Stearns" wrote:
> 
> There are book references at
> http://www.elwood.com/alu/table/books.htm#impl.  Also, section 2.2 (and
> particularly 2.2.2) of
> http://www.elwood.com/eclipse/papers/lugm98/lisp-c.html describes how
> this is done in Eclipse.

This is great.. thank you!

> First, let's think about a very C-like implementation.  
> A function (any function) is represented as some piece of
> type-tagged structure that contains a pointer to a function (i.e., the C
> function implementing the execution of the closure), and a pointer to an
> array of "bindings".

This is very close to my current model.

> Separate from the extent of the closure is the extent of the variables
> being closed over.  If the variable has dynamic extent (not the
> default), then the "binding" stored in the array can just be the address
> of the C stack-allocated variable in the surrounding function.

> However, if the variable has indefinite extent, then a separate binding
> object must be heap allocated.  The array stores the address of the
> binding, we read and write to the binding, not to some local C variable.

When do you determine which variables have dynamic extent? 
When you reach the let, by looking ahead, or when you are actually
compiling the inside closure?

I am a still a little unclear on how the function figures out which
object (and thus which variables) it is currently being executed from;
does the caller pass hidden parameters to the closure, or... ?

Thanks
Dobes
From: Howard R. Stearns
Subject: Re: Implementing Closures
Date: 
Message-ID: <37DD0FC5.28C33E51@elwood.com>
Dobes Vandermeer wrote:
> 
> "Howard R. Stearns" wrote:
> >
> > There are book references at
> > http://www.elwood.com/alu/table/books.htm#impl.  Also, section 2.2 (and
> > particularly 2.2.2) of
> > http://www.elwood.com/eclipse/papers/lugm98/lisp-c.html describes how
> > this is done in Eclipse.
> 
> This is great.. thank you!
> 
> > First, let's think about a very C-like implementation.
> > A function (any function) is represented as some piece of
> > type-tagged structure that contains a pointer to a function (i.e., the C
> > function implementing the execution of the closure), and a pointer to an
> > array of "bindings".
> 
> This is very close to my current model.
> 
> > Separate from the extent of the closure is the extent of the variables
> > being closed over.  If the variable has dynamic extent (not the
> > default), then the "binding" stored in the array can just be the address
> > of the C stack-allocated variable in the surrounding function.
> 
> > However, if the variable has indefinite extent, then a separate binding
> > object must be heap allocated.  The array stores the address of the
> > binding, we read and write to the binding, not to some local C variable.
> 
> When do you determine which variables have dynamic extent?
> When you reach the let, by looking ahead, or when you are actually
> compiling the inside closure?

The spec provides for explicit dynamic-extent declarations.  Here are
some examples:

(let ((stack-var (foo))
      (another-stack-var-because-its-not-closed-over (foo))
      (heap-var (foo)))
  (declare (dynamic-extent stack-binding))
  (flet ((heap-function (x) (frob x heap-var))
         (stack-function (x) (frob x stack-var)))
    (declare (dynamic-extent #'stack-function))
    (do-something #'heap-function #'stack-function
                  another-stack-binding-because-its-not-closed-over)))

You can also get real fancy and have your compiler try to do data-flow
analysis to figure out which function and variable bindings can be
proved to be dynamic-extent even without explicit declarations, but
that's a lot of work.


> 
> I am a still a little unclear on how the function figures out which
> object (and thus which variables) it is currently being executed from;
> does the caller pass hidden parameters to the closure, or... ?

Ah, that's another issue.  Some implementations (including the KCL
family, if I recall correctly) call each Lisp function by calling some C
function that takes an additional argument that is the "environment". 
(Some also don't take C arguments that are the same as the Lisp
arguments, but instead take an argument that is the "Lisp argument
stack."  Again, that's a side issue.)

The referenced paper, above, describes how Eclipse uses some static C
hook variables to do this without actually having to pass in an extra
environment variable.  Here are the two key paragraphs (but you'll
probably need to read the whole paper):

 
For each function defined in a non-empty environment, Eclipse
COMPILE-FILE generates two ``environment hooks''
that point to a closure's environment. One hook is defined statically,
outside the C function and the other is a local
variable within the C function definition. The static environment hook
is initialized by Eclipse when the closure
clObject is created. Generated code initializes the local environment
hook from the static hook immediately upon
entering the function on each call. All references to enclosing
variables from within the inner generated function use
this local environment hook to access the clBinding. Using a cached
local variable allows for reentrant calls to
identical code that is closed over different bindings. 

For ``top-level'' functions that only create closures once, Eclipse
initializes the static hook once and it is never
changed. For arbitrary Lisp closure objects created at run time, it is
necessary to call such functions through their
closure objects using FUNCALL or APPLY. [Of course, that's how they are
used in Lisp, too.] FUNCALL and APPLY set the environment hook if
necessary before calling the
implementing C function. The address of the static environment hook is
stored by Eclipse in the closure clObject. 

> 
> Thanks
> Dobes
From: Dobes Vandermeer
Subject: Re: Implementing Closures
Date: 
Message-ID: <37E864DC.13E8252C@mindless.com>
"Howard R. Stearns" wrote:
> 
> Generated code initializes the local environment
> hook from the static hook immediately upon
> entering the function on each call. 

This is actually the bit that seemed unclear.  When and where does the
"Generated Code" exist?

I have been giving this a lot of thought, and good sounding solution
that will work for my current implementation is this:

While compiling the inside function (which happens as a recursive
invocation of the compiler) when I encounter a variable that must be
enclosed, I compile code into the outside environment to create a
one-cell enclosed-variable object, and replace the local variable value
with this (and store the local value in the enclosed variable)

Because (setq ..) is the only way to change local variables after the
function has begun I can make setq check each variable to see if its
been enclosed, and set the variable if it has; otherwise it will set the
stack version instead.

The inside function would then have to create these variables itself,
through some kind of communication with the caller, who would have to
provide all the enclosed variables for ITS instance of that function. 
This might be done using the hooks you mentioned, or I could pass every
function its function object.  Passing the function object may assist in
debugging, and may be useful in other places as well...

CU
Dobes
From: Howard R. Stearns
Subject: Re: Implementing Closures
Date: 
Message-ID: <37E90790.629FB9AF@elwood.com>
Dobes Vandermeer wrote:
> 
> "Howard R. Stearns" wrote:
> >
> > Generated code initializes the local environment
> > hook from the static hook immediately upon
> > entering the function on each call.
> 
> This is actually the bit that seemed unclear.  When and where does the
> "Generated Code" exist?
> 
> I have been giving this a lot of thought, and good sounding solution
> that will work for my current implementation is this:
> 
> While compiling the inside function (which happens as a recursive
> invocation of the compiler) when I encounter a variable that must be
> enclosed, I compile code into the outside environment to create a
> one-cell enclosed-variable object, and replace the local variable value
> with this (and store the local value in the enclosed variable)
> 
> Because (setq ..) is the only way to change local variables after the
> function has begun I can make setq check each variable to see if its
> been enclosed, and set the variable if it has; otherwise it will set the
> stack version instead.
> 
> The inside function would then have to create these variables itself,
> through some kind of communication with the caller, who would have to
> provide all the enclosed variables for ITS instance of that function.
> This might be done using the hooks you mentioned, or I could pass every
> function its function object.  Passing the function object may assist in
> debugging, and may be useful in other places as well...

That's right.  The cleanest, most flexible and debuggable approach is to
pass the function object, or at minimum, the enclosed environment, as an
extra parameter to the C function.

In Eclipse, we wanted the C function signature to look as much like the
Lisp function as possible, and we didn't think it was nice to make C
callers have to figure out if the function being called had an enclosed
environment, and where they could get a pointer to that environment.

If the "magic" for this is of interest, here's part of the .h file that
comes with Eclipse, and which documents the low level C machinery that
does this:

/*********************************************************************
 * CAPTURED LEXICAL ENVIRONMENTS AND CLOSURES
 *
 * Each Lisp function is implemented, in part, by an clMFunction -- a
 * machine-compiled C function which returns an clObject.  For each
 * Lisp function which has been machine compiled, there is one
 * clMFunction which "does the work" of that function.
 *
 * A Lisp function object is a tagged object which can be created at
 * runtime, examined, printed, stored in variables, and passed as the
 * first argument to the functions clFuncall and clApply.  We
 * represent a Lisp function object as a clClosureCell which records
 * the type tag and the "implementing" clMFunction.  In part,
 * clFuncall works by calling the clMFunction from the clClosureCell
 * on the arguments that clFuncall is given.
 *
 * Lisp functions can be defined inside other functions, and the inner
 * definition can refer to lexical variables defined in the enclosing
 * function.  C function definitions, though, may not be nested, and
 * the C compiler can only resolve lexical references to the current
 * (inner) function -- i.e. it can only compute access to that part of
 * the stack defined by the function itself.
 *
 * If a C function needs to refer to C variables which correspond to
 * Lisp variables defined by the enclosing Lisp function, it must make
 * use of a captured lexical environment representation.
 *
 * The captured lexical environment represents the bindings in the
 * enclosing functions, not just their values.  For example, if a
 * binding is written to using clSetq() in the inner function, the value
 * of the binding in the outer function changes as well.
 *
 * Both a clMFunction and a (possibly empty) captured lexical
 * environment is necessary to fully represent a Lisp function.  The
 * clMFunction is written so as to make use of the captured lexical 
 * environment, if necessary, as stored in the closure.
 *
 * There are two interesting special cases to point out:
 *
 * 1. Consider a function which contains a loop which iterates 10
 *    times.  Within the loop, several new local variables are stack
 *    allocated.  Each time through the loop, a local function is
 *    defined which captures these new variables.  The loop then
 *    produces 10 closures, each of which captures a different set of
 *    lexical variables.  The code for each local function is the
 *    same, only the captured bindings are different.  Accordingly,
 *    only one clMFunction is defined, which is refered to by each of
 *    the 10 different clClosureCells.
 *
 * 2. The interpreter is an clMFunction.  It is designed to execute
 *    Lisp code, stored in the captured lexical environment.  Thus all
 *    Lisp interpreted function objects are clClosureCells which have
 *    the same clMFunction (the interpreter) as the stored function,
 *    and a list representation of the particular Lisp function code
 *    stored in some element of the captured lexical environment.
 *    Byte-compiled code is similar.
 *
 * Implementation:
 *
 * The captured lexical environment is an array of clBindings --
 * addresses of locations which hold clObjects.  A clClosureCell
 * includes a (possibly 0 length) array of clBindings to represent the
 * captured lexical environment.
 *
 * We could require that the environment array be passed as an
 * additional argument to the function.  However, anyone writting C
 * code which called the function directly would then have to know
 * whether or not to pass in this extra argument, and it would be poor
 * modularity for callers to have to know how a function was
 * implemented.  Instead we place a pointer to the first clBinding of
 * the environment array in a known place, unique for each clMFunction,
 * which the clMFunction knows how to gain access to.  In most cases,
 * this evironment hook never changes.  However, in the special cases
 * enumerated above, the environment does change for different
 * executions of the clMFunction.  We don't require the user to go
 * around setting this hook, but instead, we have the system functions
 * which access closures do the setting.
 *
 * Each clClosureCell stores the address of the environment hook for
 * the clMFunction.  (ex. Different closures which use the same
 * clMFunction will use the same stored environment hook address.)
 * The functions clApply(), clFuncall() and _clSetSymbolFunction()
 * each receive a closure object as an argument.  Each updates the
 * hook address specified by the closure with the address of the first
 * clBinding of the closure.  (clApply() and clFuncall() effectively
 * BIND the hook.  i.e. they set it before calling the clMFunction,
 * and reset it to the old value after the function completes.  This
 * ensures that distinct closures which use the same implementing C
 * function can call each other.)
 *
 * Operations:
 *
 * clClosureFunction(obj:clObject) => clMFunction
 *   REQUIRES: obj is a closure.
 *   EFFECTS: Returns the clMFunction which implements the closure.
 *
 * clClosureEnvironment(obj:clObject) => clBinding[]
 *   REQUIRES: obj is a closure with a non-empty captured lexical
 *     environment. 
 *   EFFECTS: Returns the address of the first clBinding in the
 *     captured lexical environment. 
 *
 * clClosureHook(obj:clObject) => clBinding **
 *   REQUIRES: obj is a closure.
 *   EFFECTS: Returns the address of the environment hook, i.e. the
 *     place where the clMFunction will expect to find the address of
 *     the first clBinding in the captured lexical environment.  This
 *     will be a clNULL_HOOK if the closure does not use a captured
 *     lexical environment. 
 *
 * clMakeClosure(n:unsigned, f:clMFunction, h:clBinding **,
 *               b0:clBinding, b1:clBinding, ...) => clObject
 *   REQUIRES: The number of bn arguments is n.  h is the address of
 *     the environment hook used by f.  f uses exactly n captured
 *     variables. 
 *   EFFECTS: Returns an clObject representing the specified closure.
 *     The location pointed to by h is initialized to point to the
 *     start of the environment within the closure.   
 *   NOTES:
 *     If f does not make use of a captured lexical environment
 *       (i.e. n=0), h may be clNULL_HOOK.  
 *   EXAMPLE: 
 *   { clObject f, x, y, *z=clMakeBinding(); int i = 4;
 *     clSetq(x, clIntFixnum(1));
 *     clSetq(y, clIntFixnum(2));
 *     clSetq(*z, clIntFixnum(3));
 *     clSetq(f, clMakeClosure(4, usrFoo, &clEnvHook(usrFoo),
 *                             &x, &y, z, (clBinding) &i)); 
 *     ... }
 *     Note that the other pointers may be cast to clBindings.
 *
 * N.B. Closures are immutable objects, and the implementation is free
 * to make copies of closures at any time. Specifically, if some C
 * program manipulates the closure fields within some closure, these
 * changes will not necessarilly propogate to all existing uses of
 * that closure.
 *********************************************************************/
....
/*********************************************************************
 * ENCLOSED ENVIRONMENTS
 *
 * All calling mechanisms (even direct and internal calls) are
 * independent of whether the called function happens to make use of
 * variables from the defining environment.
 *
 * Here is a typical C function definition for the Lisp function
 * USER:FOO which uses variables from its defining environment:
 *
 *   clDeclareEnv(Foo);
 *   clObject usrFoo(...) ...
 *   { clUseEnvironment(Foo);
 *     ... }
 *
 * Within the body of usrFoo(), clObjects in the captured lexical
 * environment may be referenced as clEnv(0, x), clEnv(1, y), etc.,
 * for the first clObject in the enclosed environment (which happens to
 * be named x), the second clObject (named y), etc.
 *
 * Data of types other than clObject can be captured in the
 * environment.  To reference them, one uses clCEnv(type, n), where
 * type is the type of the data captured.
 *
 * Dynamic-extent vs Indefinite-extent Variables:
 * 
 * The clBindings might just be the addresses of the local variables
 * in the outer function.  These are called dynamic extent variables
 * -- their extent (validity) ends when the defining code block ends
 * and the variables are "popped" from the C stack.
 *
 * However, a clClosureCell may be created and RETURNED from a
 * function.  It may then be applied or funcalled later.  In this case
 * any closed over variables must have indefinite extent -- they must
 * still be valid even after the function which first defined the
 * variables has ended.  In this case, the local variables in the
 * outer function must not simply be of type clObject, but (clObject
 * *).  We must heap allocate a clBinding to hold the pointer to the
 * clObject data.
 *
 * Operations:
 *
 * clEnv(n:unsigned, id) => clObject
 *   REQUIRES: The current function calls clUseEnvironment(), the
 *     environment defined for the current function has at least n
 *     captured bindings, and the nth binding (0 based) holds an
 *     clObject.
 *   EFFECTS: Returns the value of the nth binding from the
 *     environment for reading or writing.  (i.e. clSetq() may be used
 *     with clEnv() as the first argument.)  The ordering referred to
 *     by n is the same as the order presented to clMakeClosure(). id is
 *     ignored and serves as documentation. 
 *
 * clCEnv(val_type:type_name, n:unsigned) => value:val_type
 *   REQUIRES: The current function calls clUseEnvironment(), the
 *     environment defined for the current function has at least n
 *     captured bindings, and the nth binding (0 based) hold data of
 *     type val_type.
 *   EFFECTS: Returns the value of the nth binding from the environment
 *     for reading or writing.  (i.e. If the = operator is acceptable
 *     for data of type val_type, then = may be used with clCEnv() as
 *     the left-hand-side.)  The ordering referred to by n is the same
 *     as the order presented to for clMakeClosure().
 *   
 * clUseEnv(id:function_name)
 *   REQUIRES: clDeclareEnv(id) is used for a matching id.  Only
 *     declaration statements can precede clUseEnv() within the function
 *     in which it appears.
 *   EFFECTS: Sets up the environment within the function id() so that
 *     clEnv() and clCEnv() will work.  
 *   IMPLEMENTATION EFFECTS: clUseEnv() is defined so as to cache the
 *     address of the first clBinding when it is called, for later use
 *     by clEnv()/clCEnv().  This ensures the function will work
 *     properly even if the current clMFunction (or some function that
it
 *     calls) creates a new and different closure which happens to also
 *     use the same clMFunction.  In fact, since the environment address
 *     is cached as a lexical C varible in the funtion, it will be
 *     "reset" properly if a transfer to an exit sends us back to an
 *     earlier invokation of a function which has had its environment
 *     hook set somewheres along the way.
 *
 * clDeclareEnv(id:function_name) 
 *   REQUIRES: id is an identifier denoting a C function.
 *     clDeclareEnv() must appear at top level in the C file (i.e. like
 *     a global variable) before the definition of the function itself.
 *   EFFECTS: Establishes a static hook pointing to the environment for
 *     the specified function. clMakeClosure() initialize this variable
 *     to point to the captured lexical environment in which the C
 *     function is to be run.
 *
 * clMakeBinding() => clBinding
 *   EFFECTS: Returns an indefinite extent binding which holds an
 *     clObject.
 *   USAGE: { clObject *x = clMakeBinding(); ...} Now *x can be used
 *     exactly like any lexical clObject variable, with the additional
 *     benefit that a closure over *x (i.e. clMakeClosure(n, f, h,
 *     &*x, ...) or simply clMakeClosure(n, f, h, x, ...)) will
 *     continue to work even after the extent of *x has ended.
 *********************************************************************/

I suppose next you're going to ask about implementing multiple values,
and after that, control-stacks (unwind-protect, special binding,
catch/throw, enclosed block/tagbody...)...