[comp.lang.lisp] Common Lisp Problems with Hash-table in Macros

stephan@crin.crin.fr (Stephan BRUNESSAUX) (12/09/88)

Hi,

Is there anybody out there who can explain me
what is wrong with this example which correctly
runs in both interpreted and compiled mode on
Symbolics 3620 (Genera 7.2)
and correctly ONLY in interpreted mode on TI Explorer II
and on VAX running KCL (June 3, 1987) but not in compiled
mode !?... 

First Part:

Here is the code of a macro-function (TEST)
generating a list and a function (AUX)
to manipulate it.
Everything is okay.

   |   >(defmacro test (a b)
   |       (let ((my-list (list a b)))
   |         `(defun aux ()
   |             (mapc #'print ',my-list))))
   |   TEST
   |   
   |   >(ed "test.lsp")
   |   
   |        (test c b)   ; macro call to compile
   |   
   |        "test.lsp" [New file] 2 lines, 14 characters 
   |   
   |   >(compile-file "test")
   |   Compiling test.lsp.
   |   End of Pass 1.  
   |   End of Pass 2.  
   |   OPTIMIZE levels: Safety=0 (No runtime error checking), Space=0, Speed=3
   |   Finished compiling test.lsp.
   |   
   |   >(load "test")
   |   Loading test.o
   |   Finished loading test.o
   |   276
   |   
   |   >(aux)
   |   
   |   C
   |   B
   |   (C B)

SECOND PART:
Here come the problems. 
Now, we generate a hash-table
instead of a list.

   |   >(defmacro test (a b)  
   |      (let ((table (make-hash-table :size 1)))
   |        (setf (gethash a table) b)
   |        `(defun aux () 
   |           (maphash #'(lambda (key val) 
   |                        (format t "key: ~a - val: ~a~%" key val)) 
   |                    ',table))))
   |   TEST
   |   
   
Just try before compile.
   
   |   >(test f e)
   |   AUX
   |   
   |   >(aux)
   |   key: F - val: E					
   |   NIL            

(aux) returns :
NIL with KCL and with SYMBOLICS
#<EQL-HASH-TABLE 3625625> on TI EXPLORER


On kcl :

   |   >(compile-file "test")
   |   Compiling test.lsp.
   |   End of Pass 1.  
   |   End of Pass 2.  
   |   OPTIMIZE levels: Safety=0 (No runtime error checking), Space=0, Speed=3
   |   Finished compiling test.lsp.
   |   
   |   >(load "test")
   |   Loading test.o
   |   
   |   Error: The default dispatch macro signalled an error.
   |   Error signalled by LOAD.
   |   
   |   Broken at LOAD.  Type :H for Help.
   |   >>:b
   |   Backtrace:  > eval > LOAD
   |   
   |   >>:q

On Explorer II :

   |   > (aux)
   |   
   |   >>Trap #o50151 (WRITE-READ-ONLY M-T)
   |   There was an attempt to write into 3031065, which is a read-only address;
   |   the code was probably trying to modify a program constant.


On Symbolics :

   |   >(aux)key: F - val: G
   |   NIL

sandra%defun.utah.edu@wasatch.UUCP (Sandra J Loosemore) (12/10/88)

In article <698@crin.crin.fr> stephan@crin.crin.fr (Stephan BRUNESSAUX) writes:
>
>Is there anybody out there who can explain me
>what is wrong with this example which correctly
>runs in both interpreted and compiled mode on
>Symbolics 3620 (Genera 7.2)
>and correctly ONLY in interpreted mode on TI Explorer II
>and on VAX running KCL (June 3, 1987) but not in compiled
>mode !?... 
>
>   |   >(defmacro test (a b)  
>   |      (let ((table (make-hash-table :size 1)))
>   |        (setf (gethash a table) b)
>   |        `(defun aux () 
>   |           (maphash #'(lambda (key val) 
>   |                        (format t "key: ~a - val: ~a~%" key val)) 
>   |                    ',table))))
>   |   TEST
>   |   
>   

As it happens, this exact problem is under discussion on the
cl-compiler mailing list.  The problem is that your macro expansion
includes a quoted hash table, and not all Common Lisp implementations
currently know how to compile constant hash tables.  There is also a
fundamental problem involved: since the compiler is allowed to
"collapse" or coalesce EQUAL substructures, there is a possibility
that an EQ or EQL hash table may compile into one with fewer entries!
Another complication is that (as I understand it) KCL's compiler works
by PRINTing constant data structures to a file, and CLtL does not
require hash tables to print in such a way that they can be read in
again.  

In short, you'll have to treat it as "an error" to compile quoted hash
tables.  The ANSI Common Lisp standard may end up specifying some
other, more useful behavior, but that won't be of much use to you in
the near term. 

-Sandra Loosemore (sandra@cs.utah.edu)

jeff@aipna.ed.ac.uk (Jeff Dalton) (12/12/88)

In article <698@crin.crin.fr> stephan@crin.crin.fr (Stephan BRUNESSAUX) writes:
>Is there anybody out there who can explain me what is wrong with this
>example which correctly runs in both interpreted and compiled mode on
>Symbolics 3620 (Genera 7.2) and correctly ONLY in interpreted mode on
>TI Explorer II and on VAX running KCL (June 3, 1987) but not in compiled
>mode !?... 

>    >(defmacro test (a b)  
>       (let ((table (make-hash-table :size 1)))
>         (setf (gethash a table) b)
>         `(defun aux () 
>            (maphash #'(lambda (key val) 
>                         (format t "key: ~a - val: ~a~%" key val)) 
>                     ',table))))

The problem is that you're trying to compile some code that contains
aquoted hash table.  So, the compiler has to put a representation of
that hash table into the ".o" file.  In KCL, the hash table just gets
PRINTed and so looks like this:

   #<hash-table 0017a310>

When LOAD tries to read this, it calls READ which sees the # and calls
the reader for the # dispatching character macro.  It then sees the <
and signals an error.

I don't know what happens on the Explorer.

hall@nvuxh.UUCP (Michael R Hall) (12/13/88)

In article <698@crin.crin.fr> stephan@crin.crin.fr (Stephan BRUNESSAUX) writes:
>Is there anybody out there who can explain me what is wrong with
>this example which correctly runs in both interpreted and compiled mode on
>Symbolics 3620 (Genera 7.2) and correctly ONLY in interpreted mode on TI 
>Explorer II and on VAX running KCL (June 3, 1987) but not in compiled 
>mode !?... [Example omitted]

The example works fine, both interpreted and compiled, in Sun
(Lucid) Common Lisp version 3.4.  The problem, therefore, is KCL. 
Many Common Lisp implementations have buggy lexical closures,
especially compiled.  I don't know why, since they're not all that
hard to implement.  I suspect the KCL compiler cannot generate proper
lexical closure code, even though the lexical closure in question
references no variables outside its body.  The first example
uses #'print, which works fine in KCL; the second example uses
#'(lambda (key val) [body omitted]) and the KCL compiler barfs.  I
suggest defining a function like this
   (defun foo (key val) [body omitted])
and then referencing it as #'foo.
This, of course, should not make a difference, but it may make all
the difference to a buggy version of Common Lisp.

If all else fails, simply leave part of the code uncompiled.
-- 
Michael R. Hall              |"I live in a country that I hate.  I live in a
hall%nvuxh.UUCP@bellcore.COM | country where I want to shoot the politicians."
bellcore!nvuxh!hall          | - Peter Buck of R.E.M.

stephan@crin.crin.fr (Stephan BRUNESSAUX) (12/14/88)

In article <393@aipna.ed.ac.uk> you write:
>In article <698@crin.crin.fr> stephan@crin.crin.fr (Stephan BRUNESSAUX) writes:
>>Is there anybody out there who can explain me what is wrong with this
>>example which correctly runs in both interpreted and compiled mode on
>>Symbolics 3620 (Genera 7.2) and correctly ONLY in interpreted mode on
>>TI Explorer II and on VAX running KCL (June 3, 1987) but not in compiled
>>mode !?... 
>
>>    >(defmacro test (a b)  
>>       (let ((table (make-hash-table :size 1)))
>>         (setf (gethash a table) b)
>>         `(defun aux () 
>>            (maphash #'(lambda (key val) 
>>                         (format t "key: ~a - val: ~a~%" key val)) 
>>                     ',table))))
>
>The problem is that you're trying to compile some code that contains
>aquoted hash table.  So, the compiler has to put a representation of
>that hash table into the ".o" file.  In KCL, the hash table just gets
>PRINTed and so looks like this:
>
>   #<hash-table 0017a310>
>
>When LOAD tries to read this, it calls READ which sees the # and calls
>the reader for the # dispatching character macro.  It then sees the <
>and signals an error.
>
>I don't know what happens on the Explorer.


My first question is : why the compiler does not DUMP the hash table ?

The above example correctly runs if you supersede the hash table by a vector
(with some changes to display the contents).

On Symbolics, the hash table is DUMPed; so, you can read or modify the hash
table after loading the compiled file.
On KCL, it is impossible since it is a PRINTed representation which is
compiled.
On Explorer, the hash table seems to be dumped but no longer works since you
cannot add or remove any entries.

My second question is : Am I right to write that kind of macro ? 

My third question is : As far as I developped on Symbolics (in Common Lisp 
package) a big software using that kind of macros, is it normal to encounter
such problems on others Common Lisps ?

            Thanks for all suggestions.
                  STEPHAN.

-- 
Stephan Brunessaux
CRIN-INRIA Lorraine
BP 239 - 54506 VANDOEUVRE CEDEX - FRANCE
E-MAIL: stephan@crin.crin.fr

Krulwich-Bruce@cs.yale.edu (Bruce Krulwich) (12/14/88)

In article <290@nvuxh.UUCP>, hall@nvuxh (Michael R Hall) writes:
>The example works fine, both interpreted and compiled, in Sun
>(Lucid) Common Lisp version 3.4.  The problem, therefore, is KCL. 
>Many Common Lisp implementations have buggy lexical closures,
>especially compiled.  I don't know why, since they're not all that
>hard to implement.  I suspect the KCL compiler cannot generate proper
>lexical closure code, even though the lexical closure in question
>references no variables outside its body.  The first example
>uses #'print, which works fine in KCL; the second example uses
>#'(lambda (key val) [body omitted]) and the KCL compiler barfs.  I
>suggest defining a function like this
>   (defun foo (key val) [body omitted])
>and then referencing it as #'foo.
>This, of course, should not make a difference, but it may make all
>the difference to a buggy version of Common Lisp.

I believe that the problem has nothing to do with references out of the
closure, or really anything to do with the closure handling itself.  I think
that the problem lies in creating a hash table (or possibly another complex
data structure) at COMPILE-TIME and loading it in with the compiled code.

This is slightly related to a discussion a while ago about the environment in
which code is compiled, and the effect of compilation on the environment.  In
this case you creating the hash table in the environment in which macros are
expanded, and then saving it into a file, referencing it from code compiled in
a seperate environment in the process.  It is much more straightforward to
move the creation into the expanded macro, thus creating the table at load
time (as I showed in my previous post).

Does anyone remember the outcome of the previous discussion on compiler
environments??  Is the example that started this discussion correct within
CL??


Bruce Krulwich

pf@csc.ti.com (Paul Fuqua) (12/15/88)

    Date: Tuesday, December 13, 1988  1:15pm (CST)
    From: stephan at crin.crin.fr (Stephan BRUNESSAUX)
    Subject: Re: Common Lisp Problems with Hash-table in Macros
    Newsgroups: comp.lang.lisp

    On Explorer, the hash table seems to be dumped but no longer works since you
    cannot add or remove any entries.

The problem is that the quoted-constant hash-table is being compiled
into a read-only area, and MAPHASH errors when it tries to write to the
hash-table-lock.  Our compiler guy entered a bug-report on the problem,
since MAPHASH is not a destructive operation, so hash-tables ought to be
special-cased.  His further comments:

"This is interesting because the ANSI Common Lisp compiler subcommittee
is currently discussing rules for how constants are to be dumped and
loaded and one of the stickiest problems has been deciding which
attributes of a hash table need to be preserved.  I had thought the
issue was rather academic since I hadn't ever seen anyone try to use a
hash table as a constant before."
    
    My second question is : Am I right to write that kind of macro ? 

I would not write such a macro because any quoted constant is likely to
be write-protected -- you shouldn't be modifying a quoted constant in
your program, it's "constant."  I have a distaste for dumping complex
structures as constants in any case, preferring to write macros that
expand into construction forms that are evaluated at load time.

Paul Fuqua
Texas Instruments Computer Science Center, Dallas, Texas
CSNet:  pf@csc.ti.com (ARPA too, sometimes)
UUCP:   {smu, texsun, cs.utexas.edu, rice}!ti-csl!pf

jeff@aipna.ed.ac.uk (Jeff Dalton) (12/17/88)

In article <701@crin.crin.fr> stephan@crin.crin.fr (Stephan BRUNESSAUX) writes:
>In article <393@aipna.ed.ac.uk> you write:
>>In KCL, the hash table just gets PRINTed and so looks like this:
>>
>>   #<hash-table 0017a310>

>My first question is : why the compiler does not DUMP the hash table ?

I guess it's because someone would have to write DUMP, whatever it is.
CLtL doesn't say much about what should happen to quoted constants in
code that gets compiled.  There are some cleanup items about this 
being discussed by the X3J13 Compiler subcommittee, so it should be
clarified in the standard; but right now different implementations
do quite different things.  There has also been a proposal to give
hash tables a printed representation, by the way.

>The above example correctly runs if you supersede the hash table by a vector
>(with some changes to display the contents).

True.

>On Symbolics, the hash table is DUMPed; so, you can read or modify the hash
>table after loading the compiled file.

Some implementations might put it in a read-only area.

>On Explorer, the hash table seems to be dumped but no longer works since you
>cannot add or remove any entries.

Not surprising.

>My second question is : Am I right to write that kind of macro ? 

That will depend on what X3J13 finally says.  I think it is probably
safe to use something like "#," (or a LOAD-TIME-EVAL special form, if
it gets added to Common Lisp) to include an expression that builds the
hash table at load time.

>My third question is : As far as I developped on Symbolics (in Common Lisp 
>package) a big software using that kind of macros, is it normal to encounter
>such problems on others Common Lisps ?

Probably, because it isn't well-defined by Common Lisp: the Language,
and because people don't completely agree on what should be done.

Jeff Dalton,                      JANET: J.Dalton@uk.ac.ed             
AI Applications Institute,        ARPA:  J.Dalton%uk.ac.ed@nss.cs.ucl.ac.uk
Edinburgh University.             UUCP:  ...!ukc!ed.ac.uk!J.Dalton

jeff@aipna.ed.ac.uk (Jeff Dalton) (12/17/88)

In article <290@nvuxh.UUCP> hall%nvuxh.UUCP@bellcore.COM (Michael R Hall) writes:
>The example works fine, both interpreted and compiled, in Sun
>(Lucid) Common Lisp version 3.4.  The problem, therefore, is KCL. 

That isn't necessarily so.  This is an area in which different
Common Lisps differ and also one that is not well-defined by 
CLtL.  Besides, it is not true, in general, that when two
Common Lisp agree they are necessarily right.  KCL tends to
be a fairly minimal Common Lisp, in that it doesn't do much
beyond what's explicitly said in CLtL, and was developed by
people who knew less of the "shared cultural background"
that informs many American Common Lisps.  (For example, Mac
Lisp stored compiled constants in non-printed form, but I
don't think anything in CLtL says that is necessary.) So KCL
sometimes differs without necessarily being wrong.

>Many Common Lisp implementations have buggy lexical closures,
>especially compiled.  I don't know why, since they're not all that
>hard to implement.  I suspect the KCL compiler cannot generate proper
>lexical closure code, even though the lexical closure in question
>references no variables outside its body.  

As far as I can tell, KCL can compile code with #'(lambda ...) in
it.  Besides, I know the problem is due to something else, as
explained in an earlier message.  Your suspicion was a reasonable
one but happens to be wrong as far as KCL is concerned.