From: Robert Monfera
Subject: Subclassing on array and cons
Date: 
Message-ID: <36A17336.278C31B0@fisec.com>
It is not possible.

However, I use cons-based structures and arrays various ways and they
represent different things.  The program needs to be able to behave
differently if different structures are used, and it needs to dispatch
based on WHAT the structure represents rather than HOW (cons cells etc.)
it does it.

It is possible to create new types, but methods will not dispatch on
them, defeating the whole purpose.

My temporary solution is to create a class (e.g., 'homogeneous-tree')
with one slot called 'contents'.  It requires me, however, to recreate
many common functions.  Recursive code gets ugly when it operates on an
object like this and creates a new object instance just to pass on the
cdr.  It is a bit similar to the container(?) concept of Smalltalk and
C++, and I am not sure if I like it.

Of course, I could use 'labels', but most of my functions need to be
generic (e.g., different ways of tree traversals based on what the tree
represents).  Do not misunderstand - I do not want to dispatch on tree
contents but rather on what the tree represents (usually denoted with
type or class).  From this angle, a cons cell is a physical building
block rather than a type or class.

To summarize, I am looking for something that semantically gets closest
to:

(defclass homogeneous-tree (cons)())

I remember a couple of threads on type and built-in classes, but this
aspect did not really come up.  Please help me determine what the
options are.

Regards
Robert

From: Thomas A. Russ
Subject: Re: Subclassing on array and cons
Date: 
Message-ID: <ymir9sown5n.fsf@sevak.isi.edu>
Since as you point out you can't subclass built-in types like CONS and
ARRAY, you are left with either using some sort of CLOS class wrapper or
else a DEFSTRUCT wrapper.  Using CLOS would probably be easier, since
that way you can have slots on different classes that have the same
name.

You could build up a bit of a container hierarchy:

(defclass CONTAINER () ())

(defclass CONS-CONTAINER (CONTAINER)
   ((contents :type cons :initform NIL)))

(defclass TREE-CONTAINER (CONTAINER)    ;; Assuming this makes sense.
   ((contents :type cons :initform NIL)))

(defclass ARRAY-CONTAINER (CONTAINER)
   ((contents :type array)))

and then build your specialized types as subclasses of these classes.
I would hope that having such a hierarchy would limit the amount of
common functions that you would need to recreate, by using inheritance
from the upper levels.  You could then build your specific types as
subtypes of these containers and with a bit of care and some good luck
avoid most of the repetitive nature of the programming.

It is too bad that we can't have types like:

  (LIST FIXNUM)
  (LIST DOUBLE-FLOAT)
  etc.

and then be able to use them for dispatch.


-- 
Thomas A. Russ,  USC/Information Sciences Institute          ยทยทยท@isi.edu    
From: Robert Monfera
Subject: Re: Subclassing on array and cons
Date: 
Message-ID: <36A7D99B.B3F79105@fisec.com>
"Thomas A. Russ" wrote:
> 
> Since as you point out you can't subclass built-in types like CONS and
> ARRAY, you are left with either using some sort of CLOS class wrapper or
> else a DEFSTRUCT wrapper.  Using CLOS would probably be easier, since
> that way you can have slots on different classes that have the same
> name.
> 
> You could build up a bit of a container hierarchy:
> 
> (defclass CONTAINER () ())
> 
> (defclass CONS-CONTAINER (CONTAINER)
>    ((contents :type cons :initform NIL)))
> 
> (defclass TREE-CONTAINER (CONTAINER)    ;; Assuming this makes sense.
>    ((contents :type cons :initform NIL)))
> 
> (defclass ARRAY-CONTAINER (CONTAINER)
>    ((contents :type array)))

Thanks for the answer and the confirmation that others faced this
thought process and reached conclusions.  BTW your code is exactly what
I was talking about.  I peppered it with basic stuff like

(defmethod tree ((value cons))
  (make-instance 'tree :contents (mapaggr #'comply value)))

where mappaggr is capable of traversing any type of aggregate data
including cons, tree, array and matrix structures; GF #'comply turns the
elements into elements of complying form (e.g., mapping symbols to
objects that symbols represent).

> and then build your specialized types as subclasses of these classes.
> I would hope that having such a hierarchy would limit the amount of
> common functions that you would need to recreate, by using inheritance
> from the upper levels.  You could then build your specific types as
> subtypes of these containers and with a bit of care and some good luck
> avoid most of the repetitive nature of the programming.

That is true, and hopefully macros can be written to allow simple
recursion, mapping etc.  These macros would probably translate
ordinary-looking recursive forms to forms with specialized lambda-lists
and internal LABEL's.

> It is too bad that we can't have types like:
> 
>   (LIST FIXNUM)
>   (LIST DOUBLE-FLOAT)
>   etc.
> 
> and then be able to use them for dispatch.

Yes, this is how it should look like :-)

By the way, a bold alternative to macro wrappers could be a class that
is semantically equivalent to a cons cell, which one could specialize. 
I guess the implementation-dependent size and access time overhead would
make it impractical for larger than trivial structures, and this
approach would not work with arrays.  Even an optimized implementation
of something like this approach has to carry type information that would
definitely take more space and time than a "typeless" cons cell.  Typed
and optimized arrays already carry the information needed for
dispatching, so the prospects of dispatching on typed arrays and
possibly other containers do not seem hopeless.

Another, even less elegant and limited way of doing it is to specialize
on the first element, provided the array or cons structure is
homogenous.

One reason for raising the original question was that Ken suggested that
users of the language should use constructs that are natural to them,
while implementors make it available and efficient.  With some
thought-provoking exaggeration or misinterpretation, the reason that
"bootstrapping CLOS is easier if subclassing is not allowed" looks like
sealing fundamental classes a la Dylan for efficiency (of run-time or CL
implementation).

Certainly, people of the lisp community have thougts and practices that
I'd like to learn from.

Regards
Robert