From: Peter Seibel
Subject: Is this an abuse of CLOS?
Date: 
Message-ID: <m2n0vn7dm1.fsf@javamonkey.com>
Suppose I have a bunch of classes that can be used to represent parts
of a binary file that i'm parsing. As I parse the file, I read one
byte that tells me what the next chunk of data is and I instantiate an
instance of the appropriate class and call the generic function
'read-from' which is specialized on each type to read the right amount
of data to fill in the object. So given a function like:

  (defun read-cp-info (in)
    "Create an instance of the appropriate subclass of cp-info based on
  the data read from the current position of the stream 'in'"
    (let ((cp-info (make-cp-info (read-u1 in))))
      (read-from cp-info in)
      cp-info))

All I need is the function make-cp-info. My first attempt looked like:

  (defvar *cp-info-types* (make-array 13))
  (setf (aref *cp-info-types*  7) 'class-info)
  (setf (aref *cp-info-types*  9) 'fieldref-info)
  (setf (aref *cp-info-types* 10) 'methodref-info)
  (setf (aref *cp-info-types* 11) 'interface-methodref-info)
  (setf (aref *cp-info-types*  8) 'string-info)
  (setf (aref *cp-info-types*  3) 'integer-info)
  (setf (aref *cp-info-types*  4) 'float-info)
  (setf (aref *cp-info-types*  5) 'long-info)
  (setf (aref *cp-info-types*  6) 'double-info)
  (setf (aref *cp-info-types* 12) 'name-and-type-info)
  (setf (aref *cp-info-types*  1) 'utf8-info)

  (defun old-make-cp-info (tag)
    (make-instance (aref *cp-info-types* tag)))

Then I thought, 'Wait a sec, I can use CLOS to do the dispatching for
me', and came up with this:

  (defgeneric make-cp-info (tag)
    (:method ((tag (eql  7))) (make-instance 'class-info))
    (:method ((tag (eql  9))) (make-instance 'fieldref-info))
    (:method ((tag (eql 10))) (make-instance 'methodref-info))
    (:method ((tag (eql 11))) (make-instance 'interface-methodref-info))
    (:method ((tag (eql  8))) (make-instance 'string-info))
    (:method ((tag (eql  3))) (make-instance 'integer-info))
    (:method ((tag (eql  4))) (make-instance 'float-info))
    (:method ((tag (eql  5))) (make-instance 'long-info))
    (:method ((tag (eql  6))) (make-instance 'double-info))
    (:method ((tag (eql 12))) (make-instance 'name-and-type-info))
    (:method ((tag (eql  1))) (make-instance 'utf8-info)))


I can't decide if this latter solution is clever or stupid. (Or, for
all I know, it so obvious that it's neither clever nor stupid.) What
do you think?

-Peter

-- 
Peter Seibel
·····@javamonkey.com

From: Michael Parker
Subject: Re: Is this an abuse of CLOS?
Date: 
Message-ID: <C394CCB594EA06A9.45C6EBD66781DE58.AD6E0150FFA1DC01@lp.airnews.net>
Peter Seibel wrote:
> I can't decide if this latter solution is clever or stupid. (Or, for
> all I know, it so obvious that it's neither clever nor stupid.) What
> do you think?

That's how Keene does it in _Object-Oriented Programming in Common Lisp
- A Programmer's Guide to CLOS_, so at least there's a precedent :-)
From: Michael Parker
Subject: Re: Is this an abuse of CLOS?
Date: 
Message-ID: <B5BF7F6CF9A4AF07.29E56775A834EDE8.A1BEEDA32375F46F@lp.airnews.net>
Michael Parker wrote:
> 
> Peter Seibel wrote:
> > I can't decide if this latter solution is clever or stupid. (Or, for
> > all I know, it so obvious that it's neither clever nor stupid.) What
> > do you think?
> 
> That's how Keene does it in _Object-Oriented Programming in Common Lisp
> - A Programmer's Guide to CLOS_, so at least there's a precedent :-)

Except that she fills in the object while she's at it.  You should
to, otherwise you'll need to call yet another generic function to load
up that empty object from the binary data.
From: Erik Naggum
Subject: Re: Is this an abuse of CLOS?
Date: 
Message-ID: <3229021358194544@naggum.net>
* Peter Seibel
| Suppose I have a bunch of classes that can be used to represent parts of
| a binary file that i'm parsing.

  Been there.  Messy any way you look at it.

| As I parse the file, I read one byte that tells me what the next chunk of
| data is and I instantiate an instance of the appropriate class and call
| the generic function 'read-from' which is specialized on each type to
| read the right amount of data to fill in the object.

  It appears to me that the number of classes involved here is tied to (the
  version of) (the specification of) the binary format and is very unlikely
  to change during program execution in a way that makes a variable with a
  mapping vector meaningful, but if they change at all, and this is very
  likely to happen if my experience of the past is futurable.  (That should
  be a word.)

| So given a function like:
| 
|   (defun read-cp-info (in)
|     "Create an instance of the appropriate subclass of cp-info based on
|   the data read from the current position of the stream 'in'"
|     (let ((cp-info (make-cp-info (read-u1 in))))
|       (read-from cp-info in)
|       cp-info))

  Why not let read-from return the instance?

(read-from (make-cp-info (read-u1 in)) in)

| I can't decide if this latter solution is clever or stupid.  (Or, for all
| I know, it so obvious that it's neither clever nor stupid.)  What do you
| think?

  I think you should make a generic function read-from that specializes on
  the tag and which returns a fully constructed instance, not just an
  instance that you would call another generic function to fill in:

(defgeneric read-cp-info (tag stream))

(defmethod read-cp-info ((tag (eql 7)) (stream stream))
  (let ((... build initargs ...))
    (make-instance 'class-info ... initargs ...)))

  You would then call it with

(read-cp-info (read-u1 in) in)
  
///
-- 
  In a fight against something, the fight has value, victory has none.
  In a fight for something, the fight is a loss, victory merely relief.

  Post with compassion: http://home.chello.no/~xyzzy/kitten.jpg
From: Peter Seibel
Subject: Re: Is this an abuse of CLOS?
Date: 
Message-ID: <m2it6b77pl.fsf@javamonkey.com>
Erik Naggum <····@naggum.net> writes:

> * Peter Seibel
> | Suppose I have a bunch of classes that can be used to represent parts of
> | a binary file that i'm parsing.
> 
>   Been there.  Messy any way you look at it.

But not so bad as it is in some other languages I could name. Been
there too. ;-)

> | As I parse the file, I read one byte that tells me what the next chunk of
> | data is and I instantiate an instance of the appropriate class and call
> | the generic function 'read-from' which is specialized on each type to
> | read the right amount of data to fill in the object.
> 
>   It appears to me that the number of classes involved here is tied to (the
>   version of) (the specification of) the binary format and is very unlikely
>   to change during program execution in a way that makes a variable with a
>   mapping vector meaningful, but if they change at all, and this is very
>   likely to happen if my experience of the past is futurable.  (That should
>   be a word.)

Right. But I could always add new methods even if things did change,
just the same as I could change the mapping vector, right? Or is that
a bad idea in practice?

> | So given a function like:
> | 
> |   (defun read-cp-info (in)
> |     "Create an instance of the appropriate subclass of cp-info based on
> |   the data read from the current position of the stream 'in'"
> |     (let ((cp-info (make-cp-info (read-u1 in))))
> |       (read-from cp-info in)
> |       cp-info))
> 
>   Why not let read-from return the instance?
> 
> (read-from (make-cp-info (read-u1 in)) in)
> 
> | I can't decide if this latter solution is clever or stupid.  (Or, for all
> | I know, it so obvious that it's neither clever nor stupid.)  What do you
> | think?
> 
>   I think you should make a generic function read-from that specializes on
>   the tag and which returns a fully constructed instance, not just an
>   instance that you would call another generic function to fill in:
> 
> (defgeneric read-cp-info (tag stream))
> 
> (defmethod read-cp-info ((tag (eql 7)) (stream stream))
>   (let ((... build initargs ...))
>     (make-instance 'class-info ... initargs ...)))
> 
>   You would then call it with
> 
> (read-cp-info (read-u1 in) in)

Duh. Of course. I almost included another question about making
read-from return the instance ... it's a long story; I was trying to
solve a problem that I now think doesn't exist. But this is much
better. Thanks.

-Peter

P.S. On second thought, I will ask that other question: suppose I have
a generic function like read-from that takes an object as an argument
and I want to have all methods on that function return that object.
What's the recommended way to achieve that? Should I define a main
method that just returns the argument and then do all my
specialization in :before methods? Or is this just the wrong way of
looking at the problem?

-- 
Peter Seibel
·····@javamonkey.com
From: Erik Naggum
Subject: Re: Is this an abuse of CLOS?
Date: 
Message-ID: <3229027497471150@naggum.net>
* Peter Seibel <·····@javamonkey.com>
| Right. But I could always add new methods even if things did change, just
| the same as I could change the mapping vector, right? Or is that a bad
| idea in practice?

  I think adding a new method on read-cp-info is better.  Using a vector of
  class names spreads the information to more places that need concurrent
  updates, which I think asks for trouble in the maintenance phase.

| P.S.  On second thought, I will ask that other question: suppose I have a
| generic function like read-from that takes an object as an argument and I
| want to have all methods on that function return that object.  What's the
| recommended way to achieve that?  Should I define a main method that just
| returns the argument and then do all my specialization in :before
| methods?  Or is this just the wrong way of looking at the problem?

  I think that is a clever way of forcing a protocol, but I am not such a
  fan of forcing protocols that I think you should actually do this,
  because if anyone should write a different main method out of ignorance
  of the protocol, you are probably worse off then if you simply document
  the protocol to return the value.  You would probably have to document
  the protocol to use before methods, too.  This is a tradeoff that you
  have to make based on many additional premises, however.

///
-- 
  In a fight against something, the fight has value, victory has none.
  In a fight for something, the fight is a loss, victory merely relief.

  Post with compassion: http://home.chello.no/~xyzzy/kitten.jpg
From: Peter Seibel
Subject: Re: Is this an abuse of CLOS?
Date: 
Message-ID: <e500013c.0204290941.5f4c27e@posting.google.com>
Erik Naggum <····@naggum.net> wrote in message news:<················@naggum.net>...
> * Peter Seibel <·····@javamonkey.com>

[snip]

> | P.S.  On second thought, I will ask that other question: suppose I have a
> | generic function like read-from that takes an object as an argument and I
> | want to have all methods on that function return that object.  What's the
> | recommended way to achieve that?  Should I define a main method that just
> | returns the argument and then do all my specialization in :before
> | methods?  Or is this just the wrong way of looking at the problem?
> 
>   I think that is a clever way of forcing a protocol, but I am not such a
>   fan of forcing protocols that I think you should actually do this,
>   because if anyone should write a different main method out of ignorance
>   of the protocol, you are probably worse off then if you simply document
>   the protocol to return the value.  You would probably have to document
>   the protocol to use before methods, too.  This is a tradeoff that you
>   have to make based on many additional premises, however.

Is there anywhere in the CLHS that lists all the methods specialized
on different types of objects defined in the spec? In this case, I was
wondering if there was a way to introspect all the methods defined for
a given generic function and I figured if there was it would be a
method specialized on generic function. (The reason I was wondering
that was I figured, if there was I could pretty easily write a
regression test that checks that all the methods defined on the
generic function do live by the protocol without having to have it in
your face all the time.)

-Peter

-- 
Peter Seibel
·····@javamonkey.com
From: Barry Margolin
Subject: Re: Is this an abuse of CLOS?
Date: 
Message-ID: <Eqhz8.30$0j2.1142@paloalto-snr2.gtei.net>
In article <···························@posting.google.com>,
Peter Seibel <·····@javamonkey.com> wrote:
>Is there anywhere in the CLHS that lists all the methods specialized
>on different types of objects defined in the spec?

I'm not sure if the CLHS has it, but CLTL2 has an Index of Generic
Functions.

-- 
Barry Margolin, ······@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Kent M Pitman
Subject: Re: Is this an abuse of CLOS?
Date: 
Message-ID: <sfwr8kydyq4.fsf@shell01.TheWorld.com>
Barry Margolin <······@genuity.net> writes:

> In article <···························@posting.google.com>,
> Peter Seibel <·····@javamonkey.com> wrote:
> >Is there anywhere in the CLHS that lists all the methods specialized
> >on different types of objects defined in the spec?
> 
> I'm not sure if the CLHS has it, but CLTL2 has an Index of Generic
> Functions.

CLHS contains what ANSI CL contains; only its medium and a _very tiny_
bit of non-technical text is changed.  It's true I could have asked what
you request for CLHS but I did not happen to do so.

ANSI CL does not contain such information in order to reduce the risk
that it says something internally inconsistent by making an error in
one or the other place.  As much as possible, the standing ANSI CL
spec tries to define things only once.  This is the reason there is no
pictorial tree of classes for example; it was felt that this kind of
"extra" was matter for aftter-the-fact analysts like textbook writers
to do.  Even without such redundant info, the spec is already quite 
long (in case you hadn't noticed).  Had we begun a practice of summing
up things like this, there would have been no end to the requests for
such things to have been included.

Even so simple a thing a the index of the hardcopy document was found to
have errors by ANSI itself during the publication process and the 
publication was held up for quite some time while fixing that.  (This
particular delay turned out to be strategically quite important to the
community by an accident of fate, but in principle each such delay could
have, and would ordinarily have been, a stupid waste of time.)
From: Peter Seibel
Subject: Re: Is this an abuse of CLOS?
Date: 
Message-ID: <m2bsc17uth.fsf@javamonkey.com>
Kent M Pitman <······@world.std.com> writes:

> Barry Margolin <······@genuity.net> writes:
> 
> > In article <···························@posting.google.com>,
> > Peter Seibel <·····@javamonkey.com> wrote:
> > >Is there anywhere in the CLHS that lists all the methods specialized
> > >on different types of objects defined in the spec?
> > 
> > I'm not sure if the CLHS has it, but CLTL2 has an Index of Generic
> > Functions.
> 
> CLHS contains what ANSI CL contains; only its medium and a _very tiny_
> bit of non-technical text is changed.  It's true I could have asked what
> you request for CLHS but I did not happen to do so.
> 
> ANSI CL does not contain such information in order to reduce the risk
> that it says something internally inconsistent by making an error in
> one or the other place.  As much as possible, the standing ANSI CL
> spec tries to define things only once.  This is the reason there is no
> pictorial tree of classes for example; it was felt that this kind of
> "extra" was matter for aftter-the-fact analysts like textbook writers
> to do.  Even without such redundant info, the spec is already quite 
> long (in case you hadn't noticed).  Had we begun a practice of summing
> up things like this, there would have been no end to the requests for
> such things to have been included.
> 
> Even so simple a thing a the index of the hardcopy document was found to
> have errors by ANSI itself during the publication process and the 
> publication was held up for quite some time while fixing that.  (This
> particular delay turned out to be strategically quite important to the
> community by an accident of fate, but in principle each such delay could
> have, and would ordinarily have been, a stupid waste of time.)

[Apologies if this shows up twice--having some network flakiness.]

That makes good sense. Let me try asking a slightly different thing:
is there a standard algorithm I can perform to extract the list of
generic functions and methods defined by the spec, ideally starting
from a given type? For instance, after poking around a bit I found
Section 7.7 The Objects Dictionary; are all the standard generic
functions specified by ANSI listed there? If so I just have to go
through that page and read about them and make a cross-referenced list
by type specialized on. Or are there others listed elsewhere? (I
looked at a random sampling of other Foo Dictionaries and none of them
seemed to have any generic functions listed.)

-Peter

-- 
Peter Seibel
·····@javamonkey.com
From: Thomas F. Burdick
Subject: Re: Is this an abuse of CLOS?
Date: 
Message-ID: <xcvwuuphezp.fsf@apocalypse.OCF.Berkeley.EDU>
Peter Seibel <·····@javamonkey.com> writes:

> Let me try asking a slightly different thing: is there a standard
> algorithm I can perform to extract the list of generic functions and
> methods defined by the spec,

You could do the brute force method:

  (loop for sym being the symbols in "CL"
        for fn = (ignore-errors (symbol-function sym))
        when (typep fn 'generic-function) collect sym)

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Kent M Pitman
Subject: Re: Is this an abuse of CLOS?
Date: 
Message-ID: <sfwbsc1qy1z.fsf@shell01.TheWorld.com>
···@apocalypse.OCF.Berkeley.EDU (Thomas F. Burdick) writes:

> Peter Seibel <·····@javamonkey.com> writes:
> 
> > Let me try asking a slightly different thing: is there a standard
> > algorithm I can perform to extract the list of generic functions and
> > methods defined by the spec,
> 
> You could do the brute force method:
> 
>   (loop for sym being the symbols in "CL"
>         for fn = (ignore-errors (symbol-function sym))
>         when (typep fn 'generic-function) collect sym)

Any CL function may be implemented as a GF.  This tactic will get you a list
of things to check, IF you assume the implementation is conforming enough
that all must-be GF's are GF's.  But it will possibly get others you can't
portably depend on.
From: Thomas F. Burdick
Subject: Re: Is this an abuse of CLOS?
Date: 
Message-ID: <xcvadrlt7vx.fsf@apocalypse.OCF.Berkeley.EDU>
Kent M Pitman <······@world.std.com> writes:

> ···@apocalypse.OCF.Berkeley.EDU (Thomas F. Burdick) writes:
> 
> > Peter Seibel <·····@javamonkey.com> writes:
> > 
> > > Let me try asking a slightly different thing: is there a standard
> > > algorithm I can perform to extract the list of generic functions and
> > > methods defined by the spec,
> > 
> > You could do the brute force method:
> > 
> >   (loop for sym being the symbols in "CL"
> >         for fn = (ignore-errors (symbol-function sym))
> >         when (typep fn 'generic-function) collect sym)
> 
> Any CL function may be implemented as a GF.

Well, yes.  I was sort of assuming that the implementation didn't go
crazy and do them all as GF's, so you wouldn't get (too) many false hits.

> This tactic will get you a list of things to check, IF you assume
> the implementation is conforming enough that all must-be GF's are
> GF's.

That would be an amazingly broken implementation that didn't make
must-be GF's GF's.  Well, I guess that's not true; it could be a
really perverse one where, eg, PRINT-OBJECT is not a GF, but if you
define a method on it, magic happens that lets it work another way.

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Barry Margolin
Subject: Re: Is this an abuse of CLOS?
Date: 
Message-ID: <YDBz8.7$l86.88@paloalto-snr1.gtei.net>
In article <···············@apocalypse.OCF.Berkeley.EDU>,
Thomas F. Burdick <···@apocalypse.OCF.Berkeley.EDU> wrote:
>Kent M Pitman <······@world.std.com> writes:
>
>> ···@apocalypse.OCF.Berkeley.EDU (Thomas F. Burdick) writes:
>> 
>> > Peter Seibel <·····@javamonkey.com> writes:
>> > 
>> > > Let me try asking a slightly different thing: is there a standard
>> > > algorithm I can perform to extract the list of generic functions and
>> > > methods defined by the spec,
>> > 
>> > You could do the brute force method:
>> > 
>> >   (loop for sym being the symbols in "CL"
>> >         for fn = (ignore-errors (symbol-function sym))
>> >         when (typep fn 'generic-function) collect sym)
>> 
>> Any CL function may be implemented as a GF.
>
>Well, yes.  I was sort of assuming that the implementation didn't go
>crazy and do them all as GF's, so you wouldn't get (too) many false hits.

But consider all the functions related to conditions.  The condition system
is object-oriented, but for historical reasons we didn't specifically
require it to be based on CLOS.  However, many implementations are likely
to use that approach, so they're likely to show up.

-- 
Barry Margolin, ······@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Erik Naggum
Subject: Re: Is this an abuse of CLOS?
Date: 
Message-ID: <3229165142207948@naggum.net>
Thomas F. Burdick
|         for fn = (ignore-errors (symbol-function sym))

  You may want to check out fboundp.

///
-- 
  In a fight against something, the fight has value, victory has none.
  In a fight for something, the fight is a loss, victory merely relief.

  Post with compassion: http://home.chello.no/~xyzzy/kitten.jpg
From: Rainer Joswig
Subject: Re: Is this an abuse of CLOS?
Date: 
Message-ID: <joswig-77C20E.01093829042002@news.fu-berlin.de>
> P.S. On second thought, I will ask that other question: suppose I have
> a generic function like read-from that takes an object as an argument
> and I want to have all methods on that function return that object.
> What's the recommended way to achieve that? Should I define a main
> method that just returns the argument and then do all my
> specialization in :before methods? Or is this just the wrong way of
> looking at the problem?

You could use an around method to return the arg. Example:

(defmethod foo :around (arg)
  (call-next-method)
  arg)

(defmethod foo ((arg symbol))
  'bar)

(foo 'baz)