[comp.lang.lisp] Compiling lexical closures in Common Lisp

tim@cstr.ed.ac.uk (Tim Bradshaw) (06/12/90)

This is probably a naive question, so I apologise in advance...

In Common Lisp, if you do the following, for instance:

    (setf (symbol-function 'foo) 
      (let ((foo 0)) #'(lambda () 
                         (incf foo))))

then foo has a lexical closure as its symbol-function.  This is all
well and good.  But if I say "(compile 'foo)", in all the Common Lisps
I have tried, the compiler barfs.  On the other hand, if I stick this
in a file and compile the file, then, again in all the lisps I have
tried, I end up with a compiled closure where, significantly, the code
of the function has been compiled. (The Common Lisps I have tried are
Franz Allegro on Sun4s, kcl on Sun4s and Xerox Medley.)

Now I reckon that any symbol that has a legal function cell should be
compilable for the sake of uniformity if nothing else; and I further
reckon that I shouldn't need to write the thing out to a file to
compile it.  But obviously this isn't the case, and it would be
interesting to know why!

I know that there are ways around this problem by arranging things so
the body of the function is compiled, but it's not particularly
elegant, and I'd like to know why it has to work this way anyway.

--tim
Tim Bradshaw.  Internet: tim%ed.cstr@nsfnet-relay.ac.uk
UUCP: ...!uunet!mcvax!ukc!cstr!tim  JANET: tim@uk.ac.ed.cstr
"Quis custodiet ipsos custodes?"

MELTSNE@gecrdvm1.crd.ge.com (06/13/90)

A few more data points:
Macintosh Allegro Common LISP compiles the closure *automatically* since it
uses its fast compiler to speed up EVAL substantially. VAX Lisp says it can't
compile it because of incompatible representations (sounds like it thinks it
*should* be
able to , but can't because of implementation difficulties with closures.)  It
can compile non-closures, by the way, so you could write your code to create
a special variable, and create the accessor/modifier functions that way.  I
wrote  a dispatch table macro for VAXLISP that did worked pretty well using
that technique.

                             Ken

eb@lucid.com (Eric Benson) (06/13/90)

In article <TIM.90Jun12131548@watt.cstr.ed.ac.uk>, tim@cstr.ed.ac.uk (Tim Bradshaw) writes:
> This is probably a naive question, so I apologise in advance...
> 
> In Common Lisp, if you do the following, for instance:
> 
>     (setf (symbol-function 'foo) 
>       (let ((foo 0)) #'(lambda () 
>                          (incf foo))))
> 
> then foo has a lexical closure as its symbol-function.  This is all
> well and good.  But if I say "(compile 'foo)", in all the Common Lisps
> I have tried, the compiler barfs.  On the other hand, if I stick this
> in a file and compile the file, then, again in all the lisps I have
> tried, I end up with a compiled closure where, significantly, the code
> of the function has been compiled. (The Common Lisps I have tried are
> Franz Allegro on Sun4s, kcl on Sun4s and Xerox Medley.)
> 
> Now I reckon that any symbol that has a legal function cell should be
> compilable for the sake of uniformity if nothing else; and I further
> reckon that I shouldn't need to write the thing out to a file to
> compile it.  But obviously this isn't the case, and it would be
> interesting to know why!

This isn't really naive, it's a subtle problem and one that *could* be
fixed, but hasn't been because the level of effort far outweighs the
benefits.

The basic problem is that in most CL implementations compiled closures
have an entirely different representation from interpreted closures.
In this case you have created an interpreted closure with a reference
to the variable FOO.  This is represented using a pointer to a piece
of the interpreter environment that contains the "value cell" for FOO
as it was created when you first evaluated the LET binding.  The
interpreter knows how to find that value cell when it sees the
reference to the variable.  If the entire form is compiled, the
compiler creates its own value cell for FOO (usually considerably more
efficient than the interpreter's representation) and uses that for the
reference inside the closure.

Now, when you evaluate the form above and then try to compile *just
the lambda expression*, you are asking the compiler to use the
interpreter's value cell for FOO, as created by the LET binding in the
first place.  Naturally, this is possible (hey, it's software,
anything is possible), but it would require a change at the very heart
of the compiler and would introduce a dependency between the compiler
and interpreter that currently doesn't exist.  If someone tried to
change the environment representation in the interpreter, the compiler
would break!

I actually wrote the code in Lucid Common Lisp to deal with situation.
If you try to compile an interpreted closure with a non-empty
environment, you get the following:

> (setf (symbol-function 'foo)
        (let ((foo 0))
          #'(lambda () (incf foo))))
#<Interpreted-Function (LAMBDA NIL (INCF FOO)) 7E398F>
> (compile 'foo)
FOO has a non-empty lexical environment.
References to this environment will not be compiled correctly.
Do you wish to try compiling FOO anyway?  (Y or N): n
NIL
>

This has not been a major issue for customers (at least that I know
about) because there is always some other way to handle the problem,
either by compiling a file or by wrapping a lambda around the entire
expression, e.g.

> (funcall (compile nil
		    #'(lambda ()
			(setf (symbol-function 'foo)
			      (let ((foo 0))
				#'(lambda () (incf foo)))))))
[Now FOO is compiled.]

eb@lucid.com 	           	 	Eric Benson
415/329-8400 x5523                      Lucid, Inc.
Telex 3791739 LUCID                     707 Laurel Street
Fax 415/329-8480                        Menlo Park, CA 94025

masinter@parc.xerox.com (Larry Masinter) (06/13/90)

Most lisp compilers turn references to lexical variables -- even those
in closures -- into fixed offsets from either stack frames or the
"lexical closure" data structure. However, for this to work, the
reference to the lexical variable has to be compiled at the same time
as the binding.

This is no problem when you are compiling
(let ((foo 0))
   (setf (symbol-function test) #'(lambda () (incf foo))))

However, you can't compile test outside of the enclosing let if the
interpreter uses a different representation for closures or doesn't
build fixed-offset data structures.

It would be legal for "compile" to pretend like it succeeded when
given an interpreted closure. It would be even more reasonable if such
a compile would do the best it could, possibly generating some kind of
special code for referencing the interpreted lexical variables.

A special variable would have radically different semantics in this
case. 

I think my major advice would be to avoid calling "compile" during
runtime -- it is a useful program development tool, but has
non-portable semantics. There's probably a better way to achieve the
result you want.

X3J13 attempted to clarify the behavior of COMPILE, although I'd say
that the results are still not entirely satisfactory.

 


--
Larry Masinter (masinter@parc.xerox.com)
Xerox Palo Alto Research Center (PARC)
3333 Coyote Hill Road; Palo Alto, CA USA 94304
Fax: (415) 494-4333

ccm@warhol.ads.com (Chris McConnell) (06/14/90)

>> (funcall (compile nil
>		    #'(lambda ()
>			(setf (symbol-function 'foo)
>			      (let ((foo 0))
>				#'(lambda () (incf foo)))))))

This sort of works, only there is a problem in both lucid and
allegro.  Suppose that what you'd like to do is:

(funcall (compile nil
		    #'(lambda ()
	                (let ((*source-file* "woof"))
	                  (defun foo (x)
			    (incf x))))))

This works fine except the closure bound to foo is no longer a named
one, so debugging it can be painful.  Why would I want to do this?
Suppose I had an emacs interface that wanted to ship a region to the
LISP to get compiled and that region contained a defun.

barmar@think.com (Barry Margolin) (06/16/90)

In most Lisps, the format of environments used by interpreted and compiled
closures are different (interpreted environments are usually a list of
association lists, while compiled environments are usually arrays).  All
the lexical closures that come from the same environment must be compatible
with the environment.  Suppose you were to write:

(let ((x 0))
  (setf (symbol-function 'counter-value)
	#'(lambda () x))
  (setf (symbol-function 'increment-counter)
	#'(lambda () (incf counter))))

(compile 'counter-value)

This would presumably want to convert the lexical environment in the
closure to the compiled form, but this would cause problems for
INCREMENT-COUNTER, which expects its environment to be in the other format.

The file compiler doesn't have this problem because it is always compiling
all the functions that share a lexical environment.

I think ANSI CL will require that you be able to call COMPILE on all
functions.  However, in cases such as the above it may be implemented as a
no-op (in fact, CLtL doesn't require the compiler to do anything, it merely
suggests that compiled functions are likely to run faster).
--
Barry Margolin, Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar