From: Albert Reiner
Subject: Values that can be used in a macro's expansion
Date: 
Message-ID: <vw8wtkxfd4j.fsf@berry.phys.ntnu.no>
The following seems to work:

,----[ CMU Common Lisp Snapshot 2005-04 ]
| CL-USER> (macrolet ((foo (n)
|                       (let ((f (lambda (x) (+ x n))))
|                         `(let (r)
|                            (dotimes (i 5 r)
|                              (push (cons i (funcall ,f i)) r))))))
|            (foo 3))
| ((4 . 7) (3 . 6) (2 . 5) (1 . 4) (0 . 3))
`----

But is it guaranteed to work in a conforming implementation?

Ignoring the fact that there is no point in using a macro here in the
first place, normally I would write this as:

,----
| CL-USER> (macrolet ((foo (n) 
|                       `(labels ((f (x) (+ x ,n)))
|                          (let (r)
|                            (dotimes (i 5 r)
|                              (push (cons i (f i)) r))))))
|            (foo 3))
| ((4 . 7) (3 . 6) (2 . 5) (1 . 4) (0 . 3))
`----

so that the definition of function F is part of the expansion.

Now, however, I have come across a situation where the equivalent of F
is something like (GETHASH :FUNCTION *HASH*), and where the expansion
depends on (GETHASH :PARAMETERS *HASH*).  As the latter lookup must be
done during expansion, it is natural to do the same for the former,
too, and to use the resulting function value directly in the
expansion.

Up to now I thought that macro expansions were essentially restricted
to producing code that is like the kind of code that you can type into
the REPL; and as 2.4.8.20 prevents me from entering something like
#<Function "LAMBDA NIL" {58B02241}> or #<EQL hash table, 0 entries
{5802612D}> at the REPL, I thought I could not put those values in the
expansions.

Any clarifications would be most welcome.

Albert.

From: Pascal Bourguignon
Subject: Re: Values that can be used in a macro's expansion
Date: 
Message-ID: <8764sht7lq.fsf@thalassa.informatimago.com>
Albert Reiner <·······@tph.tuwien.ac.at> writes:

> The following seems to work:
>
> ,----[ CMU Common Lisp Snapshot 2005-04 ]
> | CL-USER> (macrolet ((foo (n)
> |                       (let ((f (lambda (x) (+ x n))))
> |                         `(let (r)
> |                            (dotimes (i 5 r)
> |                              (push (cons i (funcall ,f i)) r))))))
> |            (foo 3))
> | ((4 . 7) (3 . 6) (2 . 5) (1 . 4) (0 . 3))
> `----
>
> But is it guaranteed to work in a conforming implementation?

Yes. Just have a look at the macroexpansion!



> Ignoring the fact that there is no point in using a macro here in the
> first place, normally I would write this as:
>
> ,----
> | CL-USER> (macrolet ((foo (n) 
> |                       `(labels ((f (x) (+ x ,n)))
> |                          (let (r)
> |                            (dotimes (i 5 r)
> |                              (push (cons i (f i)) r))))))
> |            (foo 3))
> | ((4 . 7) (3 . 6) (2 . 5) (1 . 4) (0 . 3))
> `----
>
> so that the definition of function F is part of the expansion.

Actually it could have been written:

(macrolet ((foo (n)
                   (let ((f (lambda (x) (+ x n))))
                     `(let (r)
                        (dotimes (i 5 r)
                          (push (cons i (,f i)) r))))))
        (foo 3))

Since ((lambda (x) (+ x n)) i)  is a valid way to call a literal anonymous 
function.


> Now, however, I have come across a situation where the equivalent of F
> is something like (GETHASH :FUNCTION *HASH*), and where the expansion
> depends on (GETHASH :PARAMETERS *HASH*).  As the latter lookup must be
> done during expansion, it is natural to do the same for the former,
> too, and to use the resulting function value directly in the
> expansion.
>
> Up to now I thought that macro expansions were essentially restricted
> to producing code that is like the kind of code that you can type into
> the REPL; and as 2.4.8.20 prevents me from entering something like
> #<Function "LAMBDA NIL" {58B02241}> or #<EQL hash table, 0 entries
> {5802612D}> at the REPL, I thought I could not put those values in the
> expansions.
>
> Any clarifications would be most welcome.

You cannot read "#<Function "LAMBDA NIL" {58B02241}>", but you can
have the reader build a function at read-time and get it returned by
READ:

(read-from-string "#.(lambda () :hi)")
--> #<FUNCTION :LAMBDA NIL :HI> ; 17




-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
Kitty like plastic.
Confuses for litter box.
Don't leave tarp around.
From: Lars Brinkhoff
Subject: Re: Values that can be used in a macro's expansion
Date: 
Message-ID: <85hdc1m25r.fsf@junk.nocrew.org>
> Albert Reiner <·······@tph.tuwien.ac.at> writes:
>> The following seems to work:
>> | CL-USER> (macrolet ((foo (n)
>> |                       (let ((f (lambda (x) (+ x n))))
>> |                         `(let (r)
>> |                            (dotimes (i 5 r)
>> |                              (push (cons i (funcall ,f i)) r))))))
>> |            (foo 3))
>> But is it guaranteed to work in a conforming implementation?

I believe it's not guaranteed to work in compiled files, because your
funcall has a literal function argument, and that's not an
externalizable object.  See CLHS 3.2.4.2.2.

It should work fine in code that's interpreted or compiled with plain
COMPILE.

(I haven't figured out why literal functions aren't externalizable
objects.  Anyone care to explain?)

Pascal Bourguignon <····@mouse-potato.com> writes:
> Actually it could have been written:
> (macrolet ((foo (n)
>                    (let ((f (lambda (x) (+ x n))))
>                      `(let (r)
>                         (dotimes (i 5 r)
>                           (push (cons i (,f i)) r))))))
>         (foo 3))

The car of the form generated by (,f i) will be a function object, and
that's not a valid form according to CLHS 3.1.2.1.2.

I think some implementations accept such forms as an extension.

> Since ((lambda (x) (+ x n)) i)  is a valid way to call a literal anonymous 
> function.

Yes, that's a valid form, but your macro won't create such a form.
From: Thomas F. Burdick
Subject: Re: Values that can be used in a macro's expansion
Date: 
Message-ID: <xcvzmpsff4t.fsf@conquest.OCF.Berkeley.EDU>
Lars Brinkhoff <·········@nocrew.org> writes:

> (I haven't figured out why literal functions aren't externalizable
> objects.  Anyone care to explain?)

Probably because it's not clear how this can be done right.  Take this
example:

  ;; one.lisp
  (let ((x 0))
    (defun get-x ()
      x)
    (defun set-x (new)
      (setf x new)))
  
  ;; two.lisp
  (defun foo ()
    (funcall #.(function get-x)))
  
If you now do this:

  (load (compile-file "one.lisp"))
  (load (compile-file "two.lisp"))

SBCL will warn you that there were errors compiling the defun foo.
CLISP happily compiles it, so let's see what CLISP does here:

  (foo) => 0

  (set-x 10) => 10

  (foo) => 0

CLISP externalized the function object, but not in a particularly
useful way.  I'm personally a lot happier with SBCL's behavior here:

  (foo) => ;; Execution of a form compiled with errors.
  	   ;; Form:
  	   ;;   #<CLOSURE GET-X>
  	   ;; Compile-time-error:
  	   ;;   Objects of type FUNCTION can't be dumped into fasl files.
  	   ;;    [Condition of type SB-INT:COMPILED-PROGRAM-ERROR]

I'm guessing that the standards committee either thought that it's not
possible to dump function objects to fasls correctly under all
circumstances, or that asking implementors to figure out how to do
this was too high of a cost.

> Pascal Bourguignon <····@mouse-potato.com> writes:
>
> > Since ((lambda (x) (+ x n)) i)  is a valid way to call a literal anonymous 
> > function.
> 
> Yes, that's a valid form, but your macro won't create such a form.

Nor is that form a way to call a literal anonymous function.  It's a
valid way to call an anonymous (or mostly-anonymous) function, but
there are certainly no literal function objects in it.  In this case,
(lambda (x) (+ x n)) names a function, it is not the function itself.

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | Free Mumia Abu-Jamal! |
     ,--'    _,'   | Abolish the racist    |
    /       /      | death penalty!        |
   (   -.  |       `-----------------------'
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Juliusz Chroboczek
Subject: Re: Values that can be used in a macro's expansion
Date: 
Message-ID: <7iu0fzlofy.fsf@lanthane.pps.jussieu.fr>
>> (I haven't figured out why literal functions aren't externalizable
>> objects.  Anyone care to explain?)

> Probably because it's not clear how this can be done right.

I'm not sure I agree.  One could either require that only functions
defined in a null lexical environment are externalisable, or else
tweak the definition of ``equivalent as a constant'' to make CLISP's
behaviour compliant.

But there's no denying that in some implementations, requiring
functions to be externalisable would carry a large cost.  In the CMUCL
implementation (this includes SBCL), a function gets compiled to
native code, and might contain direct pointers to other functions and
data structures (due to the ``local call'' optimisation).  If
functions were to be externalisable, you'd need to keep enough data
about every function you compile to make it possible to
reverse-engineer the compiled code to indirect references to other
functions.

So there's no reason why the language definition could not provide a
consistent definition of externalising functions; but implementing
that might be tricky in some implementations.

Another reason is that if functions were internalisable, nobody would
need to learn to use MAKE-LOAD-FORM ;-)

                                        Juliusz
From: Duane Rettig
Subject: Re: Values that can be used in a macro's expansion
Date: 
Message-ID: <4r7b3jh9b.fsf@franz.com>
Juliusz Chroboczek <···@pps.jussieu.fr> writes:

>>> (I haven't figured out why literal functions aren't externalizable
>>> objects.  Anyone care to explain?)
>
>> Probably because it's not clear how this can be done right.
>
> I'm not sure I agree.  One could either require that only functions
> defined in a null lexical environment are externalisable,

We are of course only talking about compile-file here, but a look
at the constraints on compile might give us a better line to draw:
The dictionary entry for compile doesn't require a null lexical
environment, but it does say that the consequences are undefined if
the lexical environment contains bindings for anything other than
for macros, symbol-macros, or declarations.

> or else
> tweak the definition of ``equivalent as a constant'' to make CLISP's
> behaviour compliant.

I'm not sure that clisp's behavior is compliant, but perhaps not for
the reason you cite here.  Note the example I gave in my response
to Thomas Burdick - I tried this on a clisp and it didn't work as
I had expected, but since I don't know the clisp implementation,
I'm not sure why it would not have either coalesced the parts
of the closure environment that were shared by the two functions
(as is required in the first paragraph of 3.2.4) or to have called
out an error, not for the function object, which it obviously is
extending to be externalizable, but for whatever component of the
function's environment it couldn't externalize, for which it must
report an error (per the third paragraph of 3.2.4.3).

> But there's no denying that in some implementations, requiring
> functions to be externalisable would carry a large cost.  In the CMUCL
> implementation (this includes SBCL), a function gets compiled to
> native code, and might contain direct pointers to other functions and
> data structures (due to the ``local call'' optimisation).  If
> functions were to be externalisable, you'd need to keep enough data
> about every function you compile to make it possible to
> reverse-engineer the compiled code to indirect references to other
> functions.

Not necessarily.  Just as definitions of functions do not affect whether
other functions they refer to are defined or not, extenralizing a function
does not have to necessarily produce a copy for every function it
refers to.  For example, if at top level you defined a function foo
which calls bar, you don't expect bar to be automatially defined; instead
you expect that when you call foo you get an error that bar is undefined.
Likewise, capturing a literal object #'foo which calls bar might not
necessarily capture #'bar itself, but might produce a reference through
the symbol 'bar, so that you get an undefined-function _as_ _if_ you
had defined foo at top-level but not bar.

> So there's no reason why the language definition could not provide a
> consistent definition of externalising functions; but implementing
> that might be tricky in some implementations.

I think your second assertion provides the reason for the first.
The fact that functions might refer to any non-externalizable object
in the lisp means that in order to define externalizability for a
function, you'd have to define externalizablity for _all_ objects.

> Another reason is that if functions were internalisable, nobody would
> need to learn to use MAKE-LOAD-FORM ;-)

I see the smiley, and I assume you meant "externalizable", but the
statement is incomplete; only if externalizability were defined
for _all_ lisp objects would make-load-form be unnecessary.

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Thomas F. Burdick
Subject: Re: Values that can be used in a macro's expansion
Date: 
Message-ID: <xcvoe66flle.fsf@conquest.OCF.Berkeley.EDU>
Duane Rettig <·····@franz.com> writes:

> Juliusz Chroboczek <···@pps.jussieu.fr> writes:
> 
> > But there's no denying that in some implementations, requiring
> > functions to be externalisable would carry a large cost.  In the CMUCL
> > implementation (this includes SBCL), a function gets compiled to
> > native code, and might contain direct pointers to other functions and
> > data structures (due to the ``local call'' optimisation).  If
> > functions were to be externalisable, you'd need to keep enough data
> > about every function you compile to make it possible to
> > reverse-engineer the compiled code to indirect references to other
> > functions.
> 
> Not necessarily.  Just as definitions of functions do not affect whether
> other functions they refer to are defined or not, extenralizing a function
> does not have to necessarily produce a copy for every function it
> refers to.  For example, if at top level you defined a function foo
> which calls bar, you don't expect bar to be automatially defined; instead
> you expect that when you call foo you get an error that bar is undefined.
> Likewise, capturing a literal object #'foo which calls bar might not
> necessarily capture #'bar itself, but might produce a reference through
> the symbol 'bar, so that you get an undefined-function _as_ _if_ you
> had defined foo at top-level but not bar.

However, the situation that Juliusz is referring to is when #'foo
contains a reference not to #'bar, but a pointer to somewhere in the
middle of #'bar.  And if the function it contains a pointer to doesn't
have a name, or if it's an old defintion at the time that #'foo is
externalized ... what to do then?  This could all be solved, but it
*would* have to involve dumping a huge network of functions, at least
in some cases.

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | Free Mumia Abu-Jamal! |
     ,--'    _,'   | Abolish the racist    |
    /       /      | death penalty!        |
   (   -.  |       `-----------------------'
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Duane Rettig
Subject: Re: Values that can be used in a macro's expansion
Date: 
Message-ID: <4br26pbvp.fsf@franz.com>
···@conquest.OCF.Berkeley.EDU (Thomas F. Burdick) writes:

> Duane Rettig <·····@franz.com> writes:
>
>> Juliusz Chroboczek <···@pps.jussieu.fr> writes:
>> 
>> > But there's no denying that in some implementations, requiring
>> > functions to be externalisable would carry a large cost.  In the CMUCL
>> > implementation (this includes SBCL), a function gets compiled to
>> > native code, and might contain direct pointers to other functions and
>> > data structures (due to the ``local call'' optimisation).  If
>> > functions were to be externalisable, you'd need to keep enough data
>> > about every function you compile to make it possible to
>> > reverse-engineer the compiled code to indirect references to other
>> > functions.
>> 
>> Not necessarily.  Just as definitions of functions do not affect whether
>> other functions they refer to are defined or not, extenralizing a function
>> does not have to necessarily produce a copy for every function it
>> refers to.  For example, if at top level you defined a function foo
>> which calls bar, you don't expect bar to be automatially defined; instead
>> you expect that when you call foo you get an error that bar is undefined.
>> Likewise, capturing a literal object #'foo which calls bar might not
>> necessarily capture #'bar itself, but might produce a reference through
>> the symbol 'bar, so that you get an undefined-function _as_ _if_ you
>> had defined foo at top-level but not bar.
>
> However, the situation that Juliusz is referring to is when #'foo
> contains a reference not to #'bar, but a pointer to somewhere in the
> middle of #'bar.  And if the function it contains a pointer to doesn't
> have a name, or if it's an old defintion at the time that #'foo is
> externalized ... what to do then?  This could all be solved, but it
> *would* have to involve dumping a huge network of functions, at least
> in some cases.

Agreed.  Note that since Lisp itself has no "pointers", the concept
of a pointer is an extension (usually considered to be a lower-level,
implementational extension).  Consider a similar example:  What if,
during the capture of any lisp object (not necessarily a function),
the compiler encountered an unboxed double-float?  How would it
write that out to a fasl file?  I can't imagine how this might occur,
(because such a float value would have to be recognizable in its context,
and that's hard to do without it being a tagged object) but if it were
possible it would have to be "boxed" before processing, and then
re-unboxed when loaded into the target lisp.  In the same way, a lisp
which optimizes a function call with machine-addresses has a similar
problem - it may be that this address has enough context to be
considered a "boxed" value (in the sense that the block in which it
was compiled provides the proper interpretation for the address),
then of course there would have to be a way to provide a valid
value for that address when the "box" is loaded into the target lisp.
If you are getting such internal pointers from one function to
another due to block-compilation, then I would argue that the smallest
unit to be captured is that block, and a capture of a function within
that block must include the capture of the entire block.  A rather
large price to pay, but since the user has already agreed to the
reduction of dynamicity when asking for block compilation, I assume
that it is also ok to force such larger chunks into the fasl file.

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Duane Rettig
Subject: Re: Values that can be used in a macro's expansion
Date: 
Message-ID: <4vf0fjidv.fsf@franz.com>
···@conquest.OCF.Berkeley.EDU (Thomas F. Burdick) writes:

> Lars Brinkhoff <·········@nocrew.org> writes:
>
>> (I haven't figured out why literal functions aren't externalizable
>> objects.  Anyone care to explain?)
>
> Probably because it's not clear how this can be done right.

Let me preface my comments by saying that several ways to "do" this
are "right", because the spec gives a lot of leeway; the
implementation is free to externalize a non-externalizable object.

> Take this example:
>
>   ;; one.lisp
>   (let ((x 0))
>     (defun get-x ()
>       x)
>     (defun set-x (new)
>       (setf x new)))
>   
>   ;; two.lisp
>   (defun foo ()
>     (funcall #.(function get-x)))
>   
> If you now do this:
>
>   (load (compile-file "one.lisp"))
>   (load (compile-file "two.lisp"))
>
> SBCL will warn you that there were errors compiling the defun foo.
> CLISP happily compiles it, so let's see what CLISP does here:
>
>   (foo) => 0
>
>   (set-x 10) => 10
>
>   (foo) => 0
>
> CLISP externalized the function object, but not in a particularly
> useful way.

But if you assume externalizability of a function object, then there
is no requirement that the reconstructed function have identity
with the old one, since a function need not be named and thus there
is not necessarily even a way (aside from some make-load-form magic,
if one knows of the internals of the implementation) to be coalesced.

So if you look at what you've done in your example, note that there are
three functions defined - a single set-x function, and two get-x
functions - one which was defined in one.lisp and the other which was
captured and (if the implementation allows) reproduced from the function
in two.lisp.  What makes you expect that the closed-over x veraible would
be the same one in one.lisp as in the captured function in two.lisp?

I'll go back to this comment:

>>  it's not clear how this can be done right.

and suggest an alternative wording: "it's not clear how this can be
done in an intuitive manner".  I've already refuted your original
wording, but this wording I can refute in a more directed way by
using a proof-by-counterexample:

Consider the same file one.lisp and an alternate two-prime.lisp -
I'm running this on Allegro CL 7.0:

CL-USER(1): (shell "cat one.lisp")
(let ((x 0))
  (defun get-x ()
    x)
  (defun set-x (new)
    (setf x new)))
0
CL-USER(2): (shell "cat two-prime.lisp")
(defun foo ()
  (funcall #.(function get-x)))

(defun bar (new)
  (funcall #.(function set-x) new))

0
CL-USER(3): 

Note that we are capturing _both_ users of the closed-over variable
x, and assuming similarity of all structures concerned, we should be
able to see a sort of reproduction.

CL-USER(3): (load (compile-file "one.lisp"))
;;; Compiling file one.lisp
;;; Writing fasl file one.fasl
;;; Fasl write complete
; Fast loading /tmp_mnt/net/gemini/home/duane/one.fasl
T
CL-USER(4): (load (compile-file "two-prime.lisp"))
;;; Compiling file two-prime.lisp
;;; Writing fasl file two-prime.fasl
;;; Fasl write complete
; Fast loading /tmp_mnt/net/gemini/home/duane/two-prime.fasl
T
CL-USER(5): 

Note first that your original mismatch of using the reproduced
get-x (via foo) with the original set-x does not work, as with
clisp:

CL-USER(5): (foo)
0
CL-USER(6): (set-x 10)
10
CL-USER(7): (foo)
0
CL-USER(8): 

However, if I use the reproduced set-x (via bar) with the
reproduced  get-x (via foo):

CL-USER(8): (bar 20)
20
CL-USER(9): (foo)
20
CL-USER(10): 

I get what I intuitively expect.
Note also that the original closed-over x is as we had left it:

CL-USER(10): (get-x)
10
CL-USER(11): 

>  I'm personally a lot happier with SBCL's behavior here:

I'm biased, but I'm personally happier with Allegro CL's
behavior :-)

>   (foo) => ;; Execution of a form compiled with errors.
>   	   ;; Form:
>   	   ;;   #<CLOSURE GET-X>
>   	   ;; Compile-time-error:
>   	   ;;   Objects of type FUNCTION can't be dumped into fasl files.
>   	   ;;    [Condition of type SB-INT:COMPILED-PROGRAM-ERROR]
>
> I'm guessing that the standards committee either thought that it's not
> possible to dump function objects to fasls correctly under all
> circumstances, or that asking implementors to figure out how to do
> this was too high of a cost.

I wasn't on the committee, but I assume the former - function objects
carry with them an implementation-dependent amount of supporting
environment, and some of these objects might not themselves be
externalizable.  So even in an implementation which defines similarity
(and thus externalizability) for function objects, there may be
components of function objects that make them not externalizable.
But note that make-load-form fills this gap - if you know how you want
an object to be reproduced in the loading lisp (which, recall, might
not be the same lisp as the one which carried the original definitions
and which thus compiled these constants), then you can _force_ your
own style of externalizability by specifying instructions on how to
rebuild the literal objects the file-compiler encounters.

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Thomas F. Burdick
Subject: Re: Values that can be used in a macro's expansion
Date: 
Message-ID: <xcvr7b2flvy.fsf@conquest.OCF.Berkeley.EDU>
Duane Rettig <·····@franz.com> writes:

> ···@conquest.OCF.Berkeley.EDU (Thomas F. Burdick) writes:
> 
> > Lars Brinkhoff <·········@nocrew.org> writes:
> >
> >> (I haven't figured out why literal functions aren't externalizable
> >> objects.  Anyone care to explain?)
> >
> > Probably because it's not clear how this can be done right.
> 
> Let me preface my comments by saying that several ways to "do" this
> are "right", because the spec gives a lot of leeway; the
> implementation is free to externalize a non-externalizable object.

I didn't mean "right" in the sense of compliant (because if the
standard had defined similarity semantics for functions, however they
were defined would have been compliant) -- I meant it in the sense of
being intuitive and fitting into the rest of the language.

> > Take this example:
> >
> >   ;; one.lisp
> >   (let ((x 0))
> >     (defun get-x ()
> >       x)
> >     (defun set-x (new)
> >       (setf x new)))
> >   
> >   ;; two.lisp
> >   (defun foo ()
> >     (funcall #.(function get-x)))
> >   
> > If you now do this:
> >
> >   (load (compile-file "one.lisp"))
> >   (load (compile-file "two.lisp"))
> >
> > SBCL will warn you that there were errors compiling the defun foo.
> > CLISP happily compiles it, so let's see what CLISP does here:
> >
> >   (foo) => 0
> >
> >   (set-x 10) => 10
> >
> >   (foo) => 0
> >
> > CLISP externalized the function object, but not in a particularly
> > useful way.
> 
> But if you assume externalizability of a function object, then there
> is no requirement that the reconstructed function have identity
> with the old one, since a function need not be named and thus there
> is not necessarily even a way (aside from some make-load-form magic,
> if one knows of the internals of the implementation) to be coalesced.
> 
> So if you look at what you've done in your example, note that there are
> three functions defined - a single set-x function, and two get-x
> functions - one which was defined in one.lisp and the other which was
> captured and (if the implementation allows) reproduced from the function
> in two.lisp.  What makes you expect that the closed-over x veraible would
> be the same one in one.lisp as in the captured function in two.lisp?

It comes down to what you expect out of similarity.  If I can call two
functions with the same arguments in the same dynamic environment, and
I can't tell the difference between them except by using EQL, I'd call
them similar.  Otherwise, they're two totally different functions.

> I'll go back to this comment:
> 
> >>  it's not clear how this can be done right.
> 
> and suggest an alternative wording: "it's not clear how this can be
> done in an intuitive manner".  I've already refuted your original
> wording, but this wording I can refute in a more directed way by
> using a proof-by-counterexample:

You do successfully refute it for (intuitive)_Duane, but it still
fails for (intuitive)_Thomas

[snip]
> Note first that your original mismatch of using the reproduced
> get-x (via foo) with the original set-x does not work, as with
> clisp:

[snip]
> However, if I use the reproduced set-x (via bar) with the
> reproduced  get-x (via foo):

[snip]
> I get what I intuitively expect.
> Note also that the original closed-over x is as we had left it:
[snip]

In comparison to CLISP, I will admit that what Allegro CL does here is
at least useful (the reason I didn't include externalizing both get-x
and set-x was because CLISP's behavior is *really* counter-intuitive
there and I don't have ACL installed at home).

> >  I'm personally a lot happier with SBCL's behavior here:
> 
> I'm biased, but I'm personally happier with Allegro CL's
> behavior :-)

I can see how it can be convenient, but if one were to actually *use*
this feature a lot -- ie, to routinely dump sets of functions to fasl
files instead of whole applications to images -- it would lead to
periodic, infrequent, weird bugs that are really hard to track down.
I don't say "might" here, because I'm fairly certain based on
experience with a franken-lisp that heavily uses the
externalize-a-function-to-a-fasl semantics.

[snip]

> > I'm guessing that the standards committee either thought that it's not
> > possible to dump function objects to fasls correctly under all
> > circumstances, or that asking implementors to figure out how to do
> > this was too high of a cost.
> 
> I wasn't on the committee, but I assume the former - function objects
> carry with them an implementation-dependent amount of supporting
> environment, and some of these objects might not themselves be
> externalizable.  So even in an implementation which defines similarity
> (and thus externalizability) for function objects, there may be
> components of function objects that make them not externalizable.
> But note that make-load-form fills this gap - if you know how you want
> an object to be reproduced in the loading lisp (which, recall, might
> not be the same lisp as the one which carried the original definitions
> and which thus compiled these constants), then you can _force_ your
> own style of externalizability by specifying instructions on how to
> rebuild the literal objects the file-compiler encounters.

Right, the existance of make-load-form and load-time-value (because
you can't portably define a make-load-form method on FUNCTION) are the
hooks that make the standard's general refusal to make arbitrary
decisions about externalization a good one.

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | Free Mumia Abu-Jamal! |
     ,--'    _,'   | Abolish the racist    |
    /       /      | death penalty!        |
   (   -.  |       `-----------------------'
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Duane Rettig
Subject: Re: Values that can be used in a macro's expansion
Date: 
Message-ID: <4fyripcsk.fsf@franz.com>
···@conquest.OCF.Berkeley.EDU (Thomas F. Burdick) writes:

> Duane Rettig <·····@franz.com> writes:
>
>> ···@conquest.OCF.Berkeley.EDU (Thomas F. Burdick) writes:
>> 
>> > Lars Brinkhoff <·········@nocrew.org> writes:
>> >
>> >> (I haven't figured out why literal functions aren't externalizable
>> >> objects.  Anyone care to explain?)
>> >
>> > Probably because it's not clear how this can be done right.
>> 
>> Let me preface my comments by saying that several ways to "do" this
>> are "right", because the spec gives a lot of leeway; the
>> implementation is free to externalize a non-externalizable object.
>
> I didn't mean "right" in the sense of compliant (because if the
> standard had defined similarity semantics for functions, however they
> were defined would have been compliant) -- I meant it in the sense of
> being intuitive and fitting into the rest of the language.

Yes, I figured that was what you had meant, and that's why I went
into the issue about intuitive behavior.

 [ ... ]

>> So if you look at what you've done in your example, note that there are
>> three functions defined - a single set-x function, and two get-x
>> functions - one which was defined in one.lisp and the other which was
>> captured and (if the implementation allows) reproduced from the function
>> in two.lisp.  What makes you expect that the closed-over x veraible would
>> be the same one in one.lisp as in the captured function in two.lisp?
>
> It comes down to what you expect out of similarity.  If I can call two
> functions with the same arguments in the same dynamic environment, and
> I can't tell the difference between them except by using EQL, I'd call
> them similar.  Otherwise, they're two totally different functions.

If you defined, at top-level, a function twice:

(defun foo (x)
  (1+ x))

(defparameter *foo1* #'foo)

(defun foo (x)
  (1+ x))

(defparameter *foo2* #'foo)

 - What should (eq *foo1* *foo2*) return?

For reference, consider first the similar [sic] set of steps:

(defparameter *foo3* (list 'hi 'bye))

(defparameter *foo4* (list 'hi 'bye))

 - What should (eq *foo3* *foo4*) return?

 - Are *foo3* and *foo4* similar?

>> I'll go back to this comment:
>> 
>> >>  it's not clear how this can be done right.
>> 
>> and suggest an alternative wording: "it's not clear how this can be
>> done in an intuitive manner".  I've already refuted your original
>> wording, but this wording I can refute in a more directed way by
>> using a proof-by-counterexample:
>
> You do successfully refute it for (intuitive)_Duane, but it still
> fails for (intuitive)_Thomas

Well, _my_ intuition leads me along this path:

 1. When I load a file with a function defined in it, it conses up
a new one.

 2. When I load a file with a function captured in it, it conses
up a new one.

 3. When two functions share the same closed-over environment,
and one function is redefined, then the two functions no longer
share the same environment.

 4. Your original example was using an old set-x function with a
new get-x function, and so by #3 they don't share the same
environment.

Where does _your_ intuition lead you?

>> I'm biased, but I'm personally happier with Allegro CL's
>> behavior :-)
>
> I can see how it can be convenient, but if one were to actually *use*
> this feature a lot -- ie, to routinely dump sets of functions to fasl
> files instead of whole applications to images -- it would lead to
> periodic, infrequent, weird bugs that are really hard to track down.
> I don't say "might" here, because I'm fairly certain based on
> experience with a franken-lisp that heavily uses the
> externalize-a-function-to-a-fasl semantics.

I agree that using fasl files for persistence is not a great idea,
and we tend to gravitate more toward providing other ways of
achieving persistence for our users.  But I also believe that though
the spec is not always intuitive, it tends to be consistent more
often than not, and that leads me to want to trust the spec and
implementation (if it is conforming) to keep me from doing things
that lead me to such wierd bugs.  If the wierd bugs are due to the
implementation, I tend to complain to the vendor (for me, that
turns out to be a very short conversation :-), but if it is due to
my mis-interpretation of the spec, or assumptions I have made
inappropriately about what the spec allows or disallows, then I
learn my lesson and shift my intuition toward the direction that
the spec is leading me.  That way, such bugs are so infrequent as
to become one-time-only bugs in my code.

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Albert Reiner
Subject: Re: Values that can be used in a macro's expansion
Date: 
Message-ID: <vw8d5mmpqep.fsf@berry.phys.ntnu.no>
[Albert Reiner <·······@tph.tuwien.ac.at>, 01 Oct 2005 17:52:44 +0200]:
> Any clarifications would be most welcome.

Thanks for all the answers!

Albert.