From: Tony Martínez
Subject: #n= and #n# usage on instances query
Date: 
Message-ID: <b32da960.0302030640.7d6fc610@posting.google.com>
Hello all,

I've unsuccessfully searched the CLHS for textual specification of how
the following issue should be interpreted and would be very grateful
for any pointers (to the HS or otherwise) which would help me out.

Suppose #\[ and #\] are used as reader macros for "boxes"; the object
#1=[#1#] is, intuitively, "the box whose value is itself". (*)

However, I've found that CMUCL/SBCL evaluate #1=[#1#] to [#:GENSYM]
when boxes aren't implemented as lists, arrays or structs, since the
algorithm used is to leave gensym markers where the references appear
and then resolve the references by descending the data type once the
objects are read.  If boxes are implemented as instances of classes
(**) which aren't lists, arrays or structs, the gensym is inserted in
the data, but never resolved.  Clisp resolves the references no matter
what the underlying data structure.

Since I haven't found any specialization on lists, arrays and structs
in the CLHS text which specifies how #n= and #n# work, I think the
CMUCL/SBCL behaviour could be a bug.  I'd be very grateful for any
help in interpreting this behaviour and whether the CLHS does, in
fact, specify how to handle this case.

Thanks in advance!

(*) The "box" idea is a hopefully simple test case.  The real problem
is that I'm using reader macros in a state machines description
language.  Some of the machines are cyclic, which is how I ran into
this.

(**) Here's the code I'm using which does this:

(defclass box ()
 ((value :initarg :value :reader value)))

(defmethod print-object ((box box) stream)
  (format stream "[~S]" (value box)))
  
(defun read-box (stream char)
  (declare (ignore char))
  (let ((objects (read-delimited-list #\] stream t)))
    (unless (= 1 (length objects))
      (error "Unknown box reader syntax"))
    (make-instance 'box :value (first objects)))

(set-macro-character #\[ 'read-box)

(set-syntax-from-char #\] #\))

-- 
Tony

From: Kaz Kylheku
Subject: Re: #n= and #n# usage on instances query
Date: 
Message-ID: <cf333042.0302031118.652bd39c@posting.google.com>
······@terra.es (=?ISO-8859-1?Q?Tony_Mart=EDnez?=) wrote in message news:<····························@posting.google.com>...
> However, I've found that CMUCL/SBCL evaluate #1=[#1#] to [#:GENSYM]
> when boxes aren't implemented as lists, arrays or structs, since the
> algorithm used is to leave gensym markers where the references appear
> and then resolve the references by descending the data type once the
> objects are read.

Right, and if the boxes are not things that the reader's algorithm
knows how to recurse into so that it can replace its gensyms, you are
screwed. The markers stay where they are. So in other words, there
appear to be portability difficulties in making the #n= ... #n#
notation interact with user-defined reader macros. Have you tried any
other Lisps besides the two?

> If boxes are implemented as instances of classes
> (**) which aren't lists, arrays or structs, the gensym is inserted in
> the data, but never resolved.  Clisp resolves the references no matter
> what the underlying data structure.

One approach to sidestep this is to make your reader macros to expand
to a list-based representation of boxes. Then construct the real box
representation you want from that representation, using a macro. I
know, this won't do when [ ] occurs in a context in which it is not
evaluated.

Can you live with, for instance:

   '[42]  => (make-instance 'box :value 42)  ;; oops, list seen

   [42] ==> #<class box: #x1345>  ;; good, evaluation makes box

This will probably be highly portable, on the plus side.
From: Tony Martínez
Subject: Re: #n= and #n# usage on instances query
Date: 
Message-ID: <b32da960.0302040952.61ad9497@posting.google.com>
···@ashi.footprints.net (Kaz Kylheku) wrote in message news:<····························@posting.google.com>...

> [...] So in other words, there
> appear to be portability difficulties in making the #n= ... #n#
> notation interact with user-defined reader macros. Have you tried any
> other Lisps besides the two?

No, CMUCL, SBCL and Clisp are all I have access to at present.


> One approach to sidestep this is to make your reader macros to expand
> to a list-based representation of boxes. Then construct the real box
> representation you want from that representation, using a macro. I
> know, this won't do when [ ] occurs in a context in which it is not
> evaluated.
> 
> Can you live with, for instance:
> 
>    '[42]  => (make-instance 'box :value 42)  ;; oops, list seen
> 
>    [42] ==> #<class box: #x1345>  ;; good, evaluation makes box
> 
> This will probably be highly portable, on the plus side.

This is similar to what my initial version did.  I'm using reader
macros to describe state machines, to take advantage of the reader
notation for references, so I need the reader to return data
structures, not forms, to conserve cyclic data in a simple manner.  (I
think!)

My current workaround is to use my hand crufted (state-subst '((REF
. STATE) ...) <read state machine>); the state data structure is
uniform, so REFs can be anything which isn't a state.  It seems to
solve my immediate problem, even if it's as ugly as sin.

Thanks very much for the help!
From: Tony Martinez
Subject: Re: #n= and #n# usage on instances query
Date: 
Message-ID: <3E3FFAFB.50609@terra.es>
Kaz Kylheku wrote:


 > [...] So in other words, there appear to be portability difficulties
 > in making the #n= ... #n# notation interact with user-defined reader
 > macros. Have you tried any other Lisps besides the two?

No, I unfortunately only have access to cmucl/sbcl and clisp at present.

 > One approach to sidestep this is to make your reader macros to expand
 >  to a list-based representation of boxes. Then construct the real box
 >  representation you want from that representation, using a macro. I
 > know, this won't do when [ ] occurs in a context in which it is not
 > evaluated.
 >
 > Can you live with, for instance:
 >
 > '[42]  => (make-instance 'box :value 42)  ;; oops, list seen
 >
 > [42] ==> #<class box: #x1345>  ;; good, evaluation makes box
 >
 > This will probably be highly portable, on the plus side.

I started with something very much like this (my reader macros
expanded to evaluation forms, not objects), but I ended up moving away
from it. I'm trying to use reader macros to describe state machines
which may be self referential, so I need the reader to expand to objects
when read (I think!), not forms.

My current workaround is to use my hand crufted (state-subst '((REF
. STATE) ...) <read state machine>); the state data structure is
uniform, so REFs can be anything which isn't a state.  It seems to
solve my immediate problem, even if it's as ugly as sin.

Thanks very much for the help!
From: Kaz Kylheku
Subject: Re: #n= and #n# usage on instances query
Date: 
Message-ID: <cf333042.0302041634.f93fe8f@posting.google.com>
Tony Martinez <······@terra.es> wrote in message news:<··············@terra.es>...
> Kaz Kylheku wrote:
>  > Can you live with, for instance:
>  >
>  > '[42]  => (make-instance 'box :value 42)  ;; oops, list seen
>  >
>  > [42] ==> #<class box: #x1345>  ;; good, evaluation makes box
>  >
>  > This will probably be highly portable, on the plus side.
> 
> I started with something very much like this (my reader macros
> expanded to evaluation forms, not objects), but I ended up moving away
> from it. I'm trying to use reader macros to describe state machines
> which may be self referential, so I need the reader to expand to objects
> when read (I think!), not forms.

Another idea might be to hack your own equivalent of the #N= ... #N#
reader macros, but in a way that is portable, though less capable. You
could use interned symbols instead of numbers:

 #$a = [a]

Since you are doing this yourself, you can ensure that it works
properly for exactly those objects that you need it to work with in
your software, and not care about any others. E.g. just lists, vectors
and boxes.

This is almost too trivial to get right. Read the symbol, skip the =
sugar, read the object. Now munge through the object, replacing
anything that is EQ to the symbol with that object itself. The hard
part is making it work with any type, because of the lack of
reflection over slots, and such. But do you care? If you want, you can
even leave it open-ended; define a method protocol so users can add
handlers to do the substitution in their own types.

If you can't perfectly solve a language implementation problem, you
can always leave pieces of it to the programmers. ;)

Using symbols is dangerous, BTW: the symbol could unintentionally
match an occurence that is not intended to be substituted. To enforce
a readable style which minimizes errors, you could impose the rule
that the substitution symbol actually has to have a name that begins
with $:

  #$a = ($a [$a])

This is a shameful hack: the dispatch is on #$, and the symbol A is
read. You take the SYMBOL-NAME, stick a $ prefix onto and INTERN the
result to produce the symbol $A. Then you use *that* in your EQ tests
instead of the symbol A. ;)
From: Kaz Kylheku
Subject: Re: #n= and #n# usage on instances query
Date: 
Message-ID: <cf333042.0302051006.5417e4c8@posting.google.com>
Tony Martinez <······@terra.es> wrote in message news:<··············@terra.es>...
> Kaz Kylheku wrote:
> 
> 
>  > [...] So in other words, there appear to be portability difficulties
>  > in making the #n= ... #n# notation interact with user-defined reader
>  > macros. Have you tried any other Lisps besides the two?
> 
> No, I unfortunately only have access to cmucl/sbcl and clisp at present.
> 
>  > One approach to sidestep this is to make your reader macros to expand
>  >  to a list-based representation of boxes. Then construct the real box
>  >  representation you want from that representation, using a macro. I
>  > know, this won't do when [ ] occurs in a context in which it is not
>  > evaluated.
>  >
>  > Can you live with, for instance:
>  >
>  > '[42]  => (make-instance 'box :value 42)  ;; oops, list seen
>  >
>  > [42] ==> #<class box: #x1345>  ;; good, evaluation makes box
>  >
>  > This will probably be highly portable, on the plus side.
> 
> I started with something very much like this (my reader macros
> expanded to evaluation forms, not objects), but I ended up moving away
> from it. I'm trying to use reader macros to describe state machines
> which may be self referential, so I need the reader to expand to objects
> when read (I think!), not forms.

Another thing overlooked in this debate: read time evaluation. Give
the above, you can still get the object at read time using the #.[42]
syntax. Thus, you can do  #.#1=[#1] where [] by itself generates a
constructor expression. Boy, three sharps; that takes us right to the
key of A.
From: Nils Goesche
Subject: Re: #n= and #n# usage on instances query
Date: 
Message-ID: <lyu1flb9m0.fsf@cartan.de>
······@terra.es (Tony Mart�nez) writes:

> Suppose #\[ and #\] are used as reader macros for "boxes"; the
> object #1=[#1#] is, intuitively, "the box whose value is
> itself". (*)
> 
> However, I've found that CMUCL/SBCL evaluate #1=[#1#] to [#:GENSYM]
> when boxes aren't implemented as lists, arrays or structs, since the
> algorithm used is to leave gensym markers where the references
> appear and then resolve the references by descending the data type
> once the objects are read.  If boxes are implemented as instances of
> classes (**) which aren't lists, arrays or structs, the gensym is
> inserted in the data, but never resolved.  Clisp resolves the
> references no matter what the underlying data structure.
> 
> Since I haven't found any specialization on lists, arrays and
> structs in the CLHS text which specifies how #n= and #n# work, I
> think the CMUCL/SBCL behaviour could be a bug.

I can't find any such restriction, either.  And it seems that the same
bug exists in LispWorks, too.  After some playing with DISASSEMBLE, I
came up with a fix:

(defmethod do-subst ((self standard-object) alist)
  (let* ((class (class-of self))
         (slots (clos:compute-slots class)))
    (dolist (slot slots)
      (let ((name (slot-definition-name slot)))
        (when (slot-boundp self name)
          (let* ((value (slot-value self name))
                 (pair (assoc value alist :test #'eq)))
            (when pair
              (setf (slot-value self (slot-definition-name slot))
                    (cdr pair))))))))
  self)

(defmethod do-subst (self alist)
  (declare (ignore alist))
  self)

(defadvice (system::subst-refs after-advice :after) (obj alist)
  (do-subst obj alist))

I am not exactly a MOP expert, but it seems to work in LispWorks, now.
Hopefully, fixing CMUCL in a similar manner is as easy...

Regards,
-- 
Nils G�sche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x0655CFA0
From: Nils Goesche
Subject: Re: #n= and #n# usage on instances query
Date: 
Message-ID: <lylm0xb8gv.fsf@cartan.de>
I <······@cartan.de> wrote:

> (defmethod do-subst ((self standard-object) alist)

[complicated code]

>   self)

Or make that simply

(defmethod do-subst ((self standard-object) alist)
  (dolist (slot (clos:compute-slots (class-of self)) self)
    (let ((name (slot-definition-name slot)))
      (when (slot-boundp self name)
        (let ((pair (assoc (slot-value self name) alist :test #'eq)))
          (when pair
            (setf (slot-value self name) (cdr pair))))))))

Regards,
-- 
Nils G�sche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x0655CFA0
From: Tony Martínez
Subject: Re: #n= and #n# usage on instances query
Date: 
Message-ID: <b32da960.0302040945.67f06b9d@posting.google.com>
Nils Goesche <······@cartan.de> wrote in message

> (defmethod do-subst ((self standard-object) alist)
>   (dolist (slot (clos:compute-slots (class-of self)) self)
>     (let ((name (slot-definition-name slot)))
>       (when (slot-boundp self name)
>         (let ((pair (assoc (slot-value self name) alist :test #'eq)))
>           (when pair
>             (setf (slot-value self name) (cdr pair))))))))

Ah, LispWorks also succumbs!  :) SBCL doesn't have DO-SUBST, but
CIRCLE-SUBST, where the type dispatch is hard-coded.  :( 

Thanks for looking into this!
From: Nils Goesche
Subject: Re: #n= and #n# usage on instances query
Date: 
Message-ID: <ly1y2neucy.fsf@cartan.de>
······@terra.es (Tony Mart�nez) writes:

> Nils Goesche <······@cartan.de> wrote in message
> 
> > (defmethod do-subst ((self standard-object) alist)
> >   (dolist (slot (clos:compute-slots (class-of self)) self)
> >     (let ((name (slot-definition-name slot)))
> >       (when (slot-boundp self name)
> >         (let ((pair (assoc (slot-value self name) alist :test #'eq)))
> >           (when pair
> >             (setf (slot-value self name) (cdr pair))))))))
> 
> Ah, LispWorks also succumbs!  :) SBCL doesn't have DO-SUBST, but
> CIRCLE-SUBST, where the type dispatch is hard-coded.  :( 
> 
> Thanks for looking into this!

Same in LW, actually: It's hardcoded in SYSTEM::SUBST-REFS.  I worked
around it with a DEFADVICE calling my DO-SUBST method kludge /after/
SYSTEM::SUBST-REFS.

Regards,
-- 
Nils G�sche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x0655CFA0
From: Hannah Schroeter
Subject: Re: #n= and #n# usage on instances query
Date: 
Message-ID: <b1p70f$sda$1@c3po.schlund.de>
Hello!

Nils Goesche  <······@cartan.de> wrote:
>[...]

>Same in LW, actually: It's hardcoded in SYSTEM::SUBST-REFS.  I worked
>around it with a DEFADVICE calling my DO-SUBST method kludge /after/
>SYSTEM::SUBST-REFS.

Unfortunately, neither sbcl nor cmucl have something called defadvice
(checked with (apropos "advi")).

Kind regards,

Hannah.
From: Eric Marsden
Subject: Re: #n= and #n# usage on instances query
Date: 
Message-ID: <wzir8annn5l.fsf@melbourne.laas.fr>
>>>>> "hs" == Hannah Schroeter <······@schlund.de> writes:

  hs> Unfortunately, neither sbcl nor cmucl have something called defadvice
  hs> (checked with (apropos "advi")).

the ENCAPSULATE facility in CMUCL and SBCL provides DEFADVICE-like
functionality (though less sophisticated than in some other
implementations).

  <URL:http://cvs2.cons.org/ftp-area/cmucl/doc/cmu-user/debugger.html#toc84>
  
-- 
Eric Marsden                          <URL:http://www.laas.fr/~emarsden/>
From: Nils Goesche
Subject: Re: #n= and #n# usage on instances query
Date: 
Message-ID: <lysmv2dgkf.fsf@cartan.de>
······@terra.es (Tony Mart�nez) writes:

> Nils Goesche <······@cartan.de> wrote in message
> 
> > (defmethod do-subst ((self standard-object) alist)
> >   (dolist (slot (clos:compute-slots (class-of self)) self)
> >     (let ((name (slot-definition-name slot)))
> >       (when (slot-boundp self name)
> >         (let ((pair (assoc (slot-value self name) alist :test #'eq)))
> >           (when pair
> >             (setf (slot-value self name) (cdr pair))))))))
> 
> Ah, LispWorks also succumbs!  :) SBCL doesn't have DO-SUBST, but
> CIRCLE-SUBST, where the type dispatch is hard-coded.  :(
> 
> Thanks for looking into this!

Brigitte told me that my solution sucks (and she is right).  The worst
problem is that it somewhat embarassingly doesn't work if READ-BOX is
defined as

(defun read-box (stream char)
  (declare (ignore char))
  (let ((objects (read-delimited-list #\] stream t)))
    (unless (= 1 (length objects))
      (error "Unknown box reader syntax"))
    (make-instance 'box :value (list (first objects)))))

Here is my next attempt:

(defmethod do-subst ((self standard-object) alist)
  (setf (gethash self system::sharp-cons-table) t)
  (dolist (slot (class-effective-slots (class-of self)) self)
    (let ((name (slot-definition-name slot)))
      (when (slot-boundp self name)
        (setf (slot-value self name)
              (system::subst-refs (slot-value self name) alist))))))

(Rest as before).  Hopefully that's better...

Regards,
-- 
Nils G�sche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x0655CFA0