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