From: Vladimir Zolotykh
Subject: MAKE-LOAD-FORM
Date: 
Message-ID: <3EFC02B4.4090708@eurocom.od.ua>
Hi

I'm going to ask about MAKE-LOAD-FORM if you don't mind.

As far as I understand MAKE-LOAD-FORM used chiefly by the file
compiler. Program may call MAKE-LOAD-FORM but I know no useful
example of that.

The complete understanding of the negotiation between file compiler
and loader is of course beyond my abilities and I'm not going to ask
about technical details. I'd rather like to ask some practical
questions.

- The object passed to MAKE-LOAD-FORM constructed by file compiler (if
   it take place during file compilation) must be noticeably different
   from that one which will by created by the creation form returned by
   MAKE-LOAD-FORM. Probably it may have some restrictions, be read-only
   or something else. Is this true? If not I see no reason why creation
   form might ever by needed.

- Is it necessary to always surround MAKE-LOAD-FORM definition with

   (eval-when (:compile-toplevel :load-toplevel :execute)
     #|.........|#
   )

   ?

- I can read in CLTL 2nd ed. in sect. 25.1.4 that hash tables are
   supported as constants if certain requirements are met. Or, to put
   it in another word, can be serializable. Would you give me an
   example of a hash table written as a constant ?

- The same question as before for my object inherited from
   STANDARD-OBJECT e.g., obtained by DEFCLASS. What would its constant
   representation be like? E.g for structure object something like

     #S(my-struct :a 1 :b 2 :c 3)

   will do. But what's for standard object?

Thanks in advance

-- 
Vladimir Zolotykh

From: Steven M. Haflich
Subject: Re: MAKE-LOAD-FORM
Date: 
Message-ID: <3EFD9B29.5010503@alum.mit.edu>
Vladimir Zolotykh wrote:

> - The object passed to MAKE-LOAD-FORM constructed by file compiler (if
>   it take place during file compilation) must be noticeably different
>   from that one which will by created by the creation form returned by
>   MAKE-LOAD-FORM. Probably it may have some restrictions, be read-only
>   or something else. Is this true? If not I see no reason why creation
>   form might ever by needed.

No.  You provide the compiler with some code that the compiler further
transforms (mostly to handle sharing and circularity) and that code runs
at load time to create the new object.  There are no additional
restrictions placed upon that object created in the new world.  A
literal string constant in source code is recreated read-only when the
compiled file is loaded, but this is not true of objects created by
make-load-form.

> - Is it necessary to always surround MAKE-LOAD-FORM definition with
> 
>   (eval-when (:compile-toplevel :load-toplevel :execute)

It is necessary that M-L-F methods be defined at the time they are
called. i.e., at compile-file time.  How you satisfy this is up to
you.  The system provides no magic.

> - I can read in CLTL 2nd ed. in sect. 25.1.4 that hash tables are
>   supported as constants if certain requirements are met. Or, to put
>   it in another word, can be serializable. Would you give me an
>   example of a hash table written as a constant ?

The specification for treatment of a literal hash table in file-compiled
code is in ANS 3.2.4.2.2 Definition of Similarity.

A hash-table is a self-evaluating object like everything else in CL other
than conses and symbols.  For an object that do not have a readable syntax,
generally one uses a macro or symbol macroto insert it into the source code
as a literal.  The read-time eval syntax can also be used, but it is
stylistically unattractive and runs earlier, at read time rather than
macroexpand time.

(defparameter *run-time-hashtable*
   (macrolet ((my-hashtable () `',*my-hashtable*))
     (insert-my-hashtable)))

> - The same question as before for my object inherited from
>   STANDARD-OBJECT e.g., obtained by DEFCLASS. What would its constant
>   representation be like? E.g for structure object something like
> 
>     #S(my-struct :a 1 :b 2 :c 3)
> 
>   will do. But what's for standard object?

The compiler transforms the result of calling make-load-form into code that
runs at load time to construct an object in the loading lisp world that is
_similar_ to the original compile-file-time object in the compiling lisp
world.  Code is _not_ the same as a rereadable printed representation like
#S(...).
From: Kent M Pitman
Subject: Re: MAKE-LOAD-FORM
Date: 
Message-ID: <sfwznk25jyu.fsf@shell01.TheWorld.com>
"Steven M. Haflich" <·················@alum.mit.edu> writes:

> Vladimir Zolotykh wrote:
> 
> > - The object passed to MAKE-LOAD-FORM constructed by file compiler (if
> >   it take place during file compilation) must be noticeably different
> >   from that one which will by created by the creation form returned by
> >   MAKE-LOAD-FORM. Probably it may have some restrictions, be read-only
> >   or something else. Is this true? If not I see no reason why creation
> >   form might ever by needed.
> 
> No.  You provide the compiler with some code that the compiler further
> transforms (mostly to handle sharing and circularity) and that code runs
> at load time to create the new object.  There are no additional
> restrictions placed upon that object created in the new world.  A
> literal string constant in source code is recreated read-only when the
> compiled file is loaded, but this is not true of objects created by
> make-load-form.

Further, there may be user-defined constraints that the compiler cannot be
aware of.  Consider a datatype that is sometimes interned. e.g.,
        
  (defvar *frobs* (make-hash-table :test #'equal))
  
  (defclass frob ()
    ((name :accessor name :initarg :name)))

  (defmethod intern-frob (frob)
    (setf (gethash (name frob) *frobs*) frob))
  
In this case, make-load-form might need to do:

 (let ((maker `(make-instance ...)))
   (if (gethash (name frob) *frobs*)
       `(intern-frob ,maker)
     maker))

There would be no way for the system to have known about _external_
constraints on the data.

Likewise, there might be cases where certain internal data must not be
saved. Consider a "queue" that maintains:

 (defclass queue ()
   ((contents ...)
    (tail ...)))

where TAIL is maintained as a pointer to the last cdr of CONTENTS for 
efficiency.  It's simply not adequate for the compiler to do

 (make-instance 'queue :contents (list 'a 'b 'c) :tail (list 'c))

since this will not re-make the queue.  MAKE-LOAD-FORM is responsible
for assuring _internal_ interrelationships, too.

> > - Is it necessary to always surround MAKE-LOAD-FORM definition with
> >   (eval-when (:compile-toplevel :load-toplevel :execute)
> 
> It is necessary that M-L-F methods be defined at the time they are
> called. i.e., at compile-file time.  How you satisfy this is up to
> you.  The system provides no magic.

In other words, if you don't use literal instances of the class in the same
file, you don't necessarily need the EVAL-WHEN.  If you do use literal 
instances in the same file, you surely do.
 
> > - I can read in CLTL 2nd ed. in sect. 25.1.4 that hash tables are
> >   supported as constants if certain requirements are met. Or, to put
> >   it in another word, can be serializable. Would you give me an
> >   example of a hash table written as a constant ?
> 
> The specification for treatment of a literal hash table in file-compiled
> code is in ANS 3.2.4.2.2 Definition of Similarity.
> 
> A hash-table is a self-evaluating object like everything else in CL other
> than conses and symbols.  For an object that do not have a readable syntax,
> generally one uses a macro or symbol macroto insert it into the source code
> as a literal.  The read-time eval syntax can also be used, but it is
> stylistically unattractive and runs earlier, at read time rather than
> macroexpand time.
> 
> (defparameter *run-time-hashtable*
>    (macrolet ((my-hashtable () `',*my-hashtable*))
>      (insert-my-hashtable)))

Or even just

 (defun foo (x) 
   (gethash x '#.(let ((table (make-hash-table...)))
                   (setf (gethash ... table) ...)
                   (setf (gethash ... table) ...)
                   table)))

> > - The same question as before for my object inherited from
> >   STANDARD-OBJECT e.g., obtained by DEFCLASS. What would its constant
> >   representation be like? E.g for structure object something like
> >     #S(my-struct :a 1 :b 2 :c 3)
> >   will do. But what's for standard object?
> 
> The compiler transforms the result of calling make-load-form into code that
> runs at load time to construct an object in the loading lisp world that is
> _similar_ to the original compile-file-time object in the compiling lisp
> world.  Code is _not_ the same as a rereadable printed representation like
> #S(...).

It's arguably a mistake that we provide #S because it is prone to the same
error as some of the problems I cited above.  It doesn't address the
intern-frob problem.  It might address the QUEUE issue if *print-circle*
is T, but doesn't if it's NIL.  

Object identity is a complex business and it is a fact that automatic 
creation of "identical" objects is not a reliable practice because there is
no canonical definition of identical that is reliably meaningful.
See my paper
 http://www.nhplace.com/kent/PS/EQUAL.html
for an elaboration of this and some related problems to do with identity.
From: Drew McDermott
Subject: Re: MAKE-LOAD-FORM
Date: 
Message-ID: <bdmn3l$v8i$1@news.wss.yale.edu>
Vladimir Zolotykh wrote:
 > Hi
 >
 > I'm going to ask about MAKE-LOAD-FORM if you don't mind.
 >
 > As far as I understand MAKE-LOAD-FORM used chiefly by the file
 > compiler. Program may call MAKE-LOAD-FORM but I know no useful
 > example of that.

At the risk of repeating with less clarity what Steve and Kent have 
already said ....

Some hairy macros expand into code that creates complex objects at
load time.  I have (more than once) accidentally reinvented
'make-load-form' in order to produce this code.  The next time I'm in
this situation I'm going to see if 'make-load-form' is the right
vehicle for channeling code-building through.  (In my defense, I also
had to accidentally discover that 'make-load-form' even existed; it
got into the ANSI spec at the last minute, like a lot of other
extremely clever devices.)

 >
 > The complete understanding of the negotiation between file compiler
 > and loader is of course beyond my abilities and I'm not going to ask
 > about technical details. I'd rather like to ask some practical
 > questions.
 >
 > - The object passed to MAKE-LOAD-FORM constructed by file compiler (if
 >   it take place during file compilation) must be noticeably different
 >   from that one which will by created by the creation form returned by
 >   MAKE-LOAD-FORM. Probably it may have some restrictions, be read-only
 >   or something else. Is this true? If not I see no reason why creation
 >   form might ever by needed.
 >

I don't see how you can ask this question, unless you're seriously
misguided about what 'make-load-form' does.  The object it builds is
almost always different from the one it's mimicking because its whole
purpose is to rebuild that object at a future date.


 > - Is it necessary to always surround MAKE-LOAD-FORM definition with
 >
 >   (eval-when (:compile-toplevel :load-toplevel :execute)
 >     #|.........|#
 >   )
 >
 >   ?
 >

The 'make-load-form' method for objects of class C must be defined
when a file containing a constant of class C is compiled.  So if the
form (defmethod make-load-form ((x C))...) has to be in the same file,
it must be eval-when'ed in :compile-toplevel mode.

 > - I can read in CLTL 2nd ed. in sect. 25.1.4 that hash tables are
 >   supported as constants if certain requirements are met. Or, to put
 >   it in another word, can be serializable. Would you give me an
 >   example of a hash table written as a constant ?
 >
 > - The same question as before for my object inherited from
 >   STANDARD-OBJECT e.g., obtained by DEFCLASS. What would its constant
 >   representation be like? E.g for structure object something like
 >
 >     #S(my-struct :a 1 :b 2 :c 3)
 >
 >   will do. But what's for standard object?

In both cases, the answer is usually that some macro expanded into
code containing a form (quote xxx), where xxx is a hash table,
structure, or such.  E.g.,

(eval-when (:compile-toplevel)
    (defvar ht* (make-hash-table))
    (setf (gethash 'a ht*) 1)
    (setf (gethash 'b ht*) 2))

(defmacro define-ht-accessor (tabname)
    `(defun ,(build-symbol (< tabname) -ht-get) (k)
        (gethash k ',(eval tabname))))

(define-ht-accessor ht*)

If this stuff is in a file that's compiled, it will define a function
ht*-ht-get that takes a key and returns the value for that key in
table ht* at the time the compilation occurred.  This example seems
pointless, but now visualize creation of the hash-table entries being
scattered over several files.  (Or, to tie this to another thread,
image that the definition of 'ht*-ht-get' is written out at run time
to a file that is then compiled in order to capture (and freeze) the
contents of ht*.)

(The 'build-symbol' macro is a convenient way to build symbols.  It's
defined as part of the YTools package:
ftp://ftp.cs.yale.edu/pub/mcdermott/software/YTools.tar.gz)

      -- Drew McDermott