From: Albert Krewinkel
Subject: complex inheritance scheme using mixins
Date: 
Message-ID: <fwuve1o62ho.fsf@pc06.inb.uni-luebeck.de>
Hi,

during the last few days, I repeatedly encountered situations in which a
lot of functionality is provided using CLOS mixins.  A well know example
of this is Sonja Keenes CLOS implementation of streams:
input/output/bidirectional streams for disk/tape and element-type
8-bit-byte/32-bit-word/character.  To allow for all possible
combinations of direction, device-type and element-type, there is a
total of 15 instantiable classes (no bidirectional streams for tapes).

In the process of defining these 15 classes, 27 more have to be defined,
such that the 15 can be build by inheriting from those.  Most of the
total 42 classes don't even define any additional slots.  Writing them
results in a lot of boilerplate.

This observation made me think of possible simplifications in this
process: I feel like there's missing some method to generate "cross
products" of inheritance trees/graphs.  Let me explain using the example
of disk streams:

Streams have a basic class layout of the following form:

            stream
           /     \
input-stream     output-stream
           \     /
      bidirectional-stream


Now instead of having to write all disk-streams following the above
scheme by hand, I'd like to be able to do

 (define-cross-product-classes stream-class-graph disk)

and thereby get all the above stream classes s/stream/disk-stream/g
Finally, all that's left to do is
 (define-cross-product stream-classes device-classes element-types)
and - magic - all necessary classes are generated.  Only those with
additional slots would have to be defined by hand.

Now this doesn't seem easy, but I'm currently facing an application
where this would be more than usefull.  It would also make such schemes
more easily extensible, since one doesn't have to remember which
intermediate classes one has to write: Just define another device-type
and apply the crossproduct -- done.

Has that ever been done before?  Are there any research papers I should
look at?  This is more complex than I thought, so any help is highly
appreciated.

Thanks in advance
Albert

From: ······@corporate-world.lisp.de
Subject: Re: complex inheritance scheme using mixins
Date: 
Message-ID: <2d71bcfa-2b97-4258-8ce4-4bf9b13b94b1@b1g2000hsg.googlegroups.com>
On May 8, 9:05 pm, Albert Krewinkel <·········@gmx.net> wrote:
> Hi,
>
> during the last few days, I repeatedly encountered situations in which a
> lot of functionality is provided using CLOS mixins.  A well know example
> of this is Sonja Keenes CLOS implementation of streams:
> input/output/bidirectional streams for disk/tape and element-type
> 8-bit-byte/32-bit-word/character.  To allow for all possible
> combinations of direction, device-type and element-type, there is a
> total of 15 instantiable classes (no bidirectional streams for tapes).
>
> In the process of defining these 15 classes, 27 more have to be defined,
> such that the 15 can be build by inheriting from those.  Most of the
> total 42 classes don't even define any additional slots.  Writing them
> results in a lot of boilerplate.
>
> This observation made me think of possible simplifications in this
> process: I feel like there's missing some method to generate "cross
> products" of inheritance trees/graphs.  Let me explain using the example
> of disk streams:
>
> Streams have a basic class layout of the following form:
>
>             stream
>            /     \
> input-stream     output-stream
>            \     /
>       bidirectional-stream
>
> Now instead of having to write all disk-streams following the above
> scheme by hand, I'd like to be able to do
>
>  (define-cross-product-classes stream-class-graph disk)
>
> and thereby get all the above stream classes s/stream/disk-stream/g
> Finally, all that's left to do is
>  (define-cross-product stream-classes device-classes element-types)
> and - magic - all necessary classes are generated.  Only those with
> additional slots would have to be defined by hand.
>
> Now this doesn't seem easy, but I'm currently facing an application
> where this would be more than usefull.  It would also make such schemes
> more easily extensible, since one doesn't have to remember which
> intermediate classes one has to write: Just define another device-type
> and apply the crossproduct -- done.
>
> Has that ever been done before?  Are there any research papers I should
> look at?  This is more complex than I thought, so any help is highly
> appreciated.
>
> Thanks in advance
> Albert

Not really answering your question, but a few thoughts:

Multiple-Inheritance and Mixins are slightly different concepts.
The example above you mention is more about multiple-inheritance.

In a typical 'Mixin' scenario you would have a few main classes.
Like STREAM or, say, STANDARD-STREAM. You would then write
mixins that would implement certain functionality like:
binary-io-mixin, text-io-mixin, buffered-io-mixin, caching-mixin, etc.
These mixins cannot / should not be instatiated. They may add slots,
add methods or extend methods. Sometimes a mixin may inherit from
other mixins. There are also strange cases where a mixin inherits
from a normal class (one that can/should be instantiated)
and makes it into a mixin (one that cannot / should not be
instantiated).

Then you would define the basic-stream class to inherit from
stream and the mixins you want to present in a basic stream class.

You would not generate classes for all combinations. The mixins
are just building blocks that can be combined into a small
number of useful classes. If for a special requirement a new
class is needed, it is then defined by the user of the
library and then this new class will inherit from a normal class
and add the necessary mixins. Sometimes you might want
provide two (or more) alternative implementations for a mixin
functionality. You can just swap them by swapping the inherited
mixins for some class in one place.

In some earlier object systems for Lisp, one also allowed a class
to only inherit from one 'normal' class, but to inherit from
multiple mixin classes. This had efficiency reasons and it
was thought that the class architecture would be better then.
From: Ken Tilton
Subject: Re: complex inheritance scheme using mixins
Date: 
Message-ID: <48236424$0$25047$607ed4bc@cv.net>
Albert Krewinkel wrote:
> Hi,
> 
> during the last few days, I repeatedly encountered situations in which a
> lot of functionality is provided using CLOS mixins.  A well know example
> of this is Sonja Keenes CLOS implementation of streams:
> input/output/bidirectional streams for disk/tape and element-type
> 8-bit-byte/32-bit-word/character.  To allow for all possible
> combinations of direction, device-type and element-type, there is a
> total of 15 instantiable classes (no bidirectional streams for tapes).
> 
> In the process of defining these 15 classes, 27 more have to be defined,
> such that the 15 can be build by inheriting from those.  Most of the
> total 42 classes don't even define any additional slots.  Writing them
> results in a lot of boilerplate.
> 
> This observation made me think of possible simplifications in this
> process: I feel like there's missing some method to generate "cross
> products" of inheritance trees/graphs.  Let me explain using the example
> of disk streams:
> 
> Streams have a basic class layout of the following form:
> 
>             stream
>            /     \
> input-stream     output-stream
>            \     /
>       bidirectional-stream
> 
> 
> Now instead of having to write all disk-streams following the above
> scheme by hand, I'd like to be able to do
> 
>  (define-cross-product-classes stream-class-graph disk)
> 
> and thereby get all the above stream classes s/stream/disk-stream/g
> Finally, all that's left to do is
>  (define-cross-product stream-classes device-classes element-types)
> and - magic - all necessary classes are generated.  Only those with
> additional slots would have to be defined by hand.
> 
> Now this doesn't seem easy, but I'm currently facing an application
> where this would be more than usefull.  It would also make such schemes
> more easily extensible, since one doesn't have to remember which
> intermediate classes one has to write: Just define another device-type
> and apply the crossproduct -- done.
> 
> Has that ever been done before?  Are there any research papers I should
> look at?  This is more complex than I thought, so any help is highly
> appreciated.

I recall a "capabilities" project that supported dynamically mixing and 
unmixing in classes, I guess that would support what you want. I have 
not found in recent searches, but others here have talked about using 
the mop for this so you'll probably get more useful info soon.

I will be here reminding you not to lock into CLOS as a key component of 
your eventual solutio:, Lisp is a big language with lotsa ways of doing 
things. I am made especially nervous by the idea of "the ones needing 
slots" getting special handling.

btw, if you just need all the combos combinated (?), take your define 
cross-product macro and have it combinate the mixins listed at macro 
expansion time and just have that emit one big progn of defclasses, one 
per combo (I am sure you jsut need unique combos). Then this geenrator 
can take a following list of specifications in the form:

   (slots (<specific mixins>+)
     <slots-needed>+)

When the huge class combinator is generating each class it can peek at 
the "slots" specifications to see if there is a match and throw those 
slots in at the same time.

kt

ps. come to think of it, I am surprised a /combination/ of mixins needs 
a new slot, sounds like you will not really get all you need from this 
mixology, which brings me back to warning you against locking in too 
soon on this approach. k

-- 
http://smuglispweeny.blogspot.com/
http://www.theoryyalgebra.com/
ECLM rant: 
http://video.google.com/videoplay?docid=-1331906677993764413&hl=en
ECLM talk: 
http://video.google.com/videoplay?docid=-9173722505157942928&q=&hl=en
From: Albert Krewinkel
Subject: Re: complex inheritance scheme using mixins
Date: 
Message-ID: <fwu3aoswkey.fsf@pc06.inb.uni-luebeck.de>
Ken Tilton <···········@optonline.net> writes:

> Albert Krewinkel wrote:
>> [...] I feel like there's missing some method to generate "cross
>> products" of inheritance trees/graphs.
[...]
>> Finally, all that's left to do is
>>  (define-cross-product stream-classes device-classes element-types)
>> and - magic - all necessary classes are generated.  Only those with
>> additional slots would have to be defined by hand.
>
> I recall a "capabilities" project that supported dynamically mixing
> and unmixing in classes, I guess that would support what you want. I
> have not found in recent searches, but others here have talked about
> using the mop for this so you'll probably get more useful info soon.

Whoever came up with the name "capabilities" for this project seemingly
didn't want any users.  The words 'lisp' and 'capabilities' never are
mentioned on same page, are they?  Googeling this is a PITA.

> I will be here reminding you not to lock into CLOS as a key component
> of your eventual solutio:, Lisp is a big language with lotsa ways of
> doing things. I am made especially nervous by the idea of "the ones
> needing slots" getting special handling.

Disallowing adding of slots to composed class should make special
handling unnecessary.  Classes with slots would be 'base classes', and
those will have to be defined anyway.

> btw, if you just need all the combos combinated (?), take your define
> cross-product macro and have it combinate the mixins listed at macro
> expansion time and just have that emit one big progn of defclasses,
> one per combo (I am sure you jsut need unique combos). Then this
> geenrator can take a following list of specifications in the form:
>
>   (slots (<specific mixins>+)
>     <slots-needed>+)
>
> When the huge class combinator is generating each class it can peek at
> the "slots" specifications to see if there is a match and throw those
> slots in at the same time.

Specifying newly generated classes using the slots needed is a nice
idea, I will have to look into this further.

Thanks for the help!

Albert

PS: Nice (and entertaining) rant, BTW
From: Ken Tilton
Subject: Re: complex inheritance scheme using mixins
Date: 
Message-ID: <4823819f$0$11636$607ed4bc@cv.net>
Albert Krewinkel wrote:
> Ken Tilton <···········@optonline.net> writes:
> 
> 
>>Albert Krewinkel wrote:
>>
>>>[...] I feel like there's missing some method to generate "cross
>>>products" of inheritance trees/graphs.
> 
> [...]
> 
>>>Finally, all that's left to do is
>>> (define-cross-product stream-classes device-classes element-types)
>>>and - magic - all necessary classes are generated.  Only those with
>>>additional slots would have to be defined by hand.
>>
>>I recall a "capabilities" project that supported dynamically mixing
>>and unmixing in classes, I guess that would support what you want. I
>>have not found in recent searches, but others here have talked about
>>using the mop for this so you'll probably get more useful info soon.
> 
> 
> Whoever came up with the name "capabilities" for this project seemingly
> didn't want any users.  The words 'lisp' and 'capabilities' never are
> mentioned on same page, are they?  Googeling this is a PITA.

Yeah, I just struck out again, too. I tried throwing in "Northwestern" 
and "Chicago", ISTR the gentleman being from thereabouts. No luck. There 
was a full-blown paper, and I found this all back in the mid-nineties, 
if that helps. Also not succeeding was looking for me mentioning on this 
list back then, when I might have had the link and posted it.

Anyway, Tilton's Law of Lisp Coolness says it is cooler to do something 
without the MOP if possible: ie, like macros, the MOP should be used 
only if necessary. In this case I do not hear you needing these mixes to 
be mixed dynamically at run time, so I would just write a macro to 
generate all the combinations from a hard-coded list presented in order 
of decreasing precedence desired (if that is an issue).

> 
> 
>>I will be here reminding you not to lock into CLOS as a key component
>>of your eventual solutio:, Lisp is a big language with lotsa ways of
>>doing things. I am made especially nervous by the idea of "the ones
>>needing slots" getting special handling.
> 
> 
> Disallowing adding of slots to composed class should make special
> handling unnecessary.  Classes with slots would be 'base classes', and
> those will have to be defined anyway.
> 
> 
>>btw, if you just need all the combos combinated (?), take your define
>>cross-product macro and have it combinate the mixins listed at macro
>>expansion time and just have that emit one big progn of defclasses,
>>one per combo (I am sure you jsut need unique combos). Then this
>>geenrator can take a following list of specifications in the form:
>>
>>  (slots (<specific mixins>+)
>>    <slots-needed>+)
>>
>>When the huge class combinator is generating each class it can peek at
>>the "slots" specifications to see if there is a match and throw those
>>slots in at the same time.
> 
> 
> Specifying newly generated classes using the slots needed is a nice
> idea, I will have to look into this further.
> 
> Thanks for the help!
> 
> Albert
> 
> PS: Nice (and entertaining) rant, BTW

Thanks! Sounds like you are working on an Application, His Kennyness is 
pleased.

:)

kenny

-- 
http://smuglispweeny.blogspot.com/
http://www.theoryyalgebra.com/
ECLM rant: 
http://video.google.com/videoplay?docid=-1331906677993764413&hl=en
ECLM talk: 
http://video.google.com/videoplay?docid=-9173722505157942928&q=&hl=en
From: Madhu
Subject: Re: complex inheritance scheme using mixins
Date: 
Message-ID: <m3d4nwtey6.fsf@meer.net>
[Just an experience report, YMMV]

* Albert Krewinkel <···············@pc06.inb.uni-luebeck.de> :
Wrote on Thu, 08 May 2008 21:05:07 +0200:

[...]
| Now instead of having to write all disk-streams following the above
| scheme by hand, I'd like to be able to do
|
|  (define-cross-product-classes stream-class-graph disk)

I have more than once felt I needed something like this.  However,
invariably I've ended up abandoning defining classes via ENSURE-CLASS
mechanisms, preferring instead the explicit DEFCLASS definitions in a
static file [which the editor can jump to].  The rules of my
`define-cross-product' equivalent however turned out to be useful to
both generate the boiler plate which generated the file initially (few
iterations before settling on class names) and also to write tests to
check the class structure of the [semi automatically generated] classes.

However in all my cases, the class relations were fairly static, I
suspect they are in your case too.  For the other case, see

See the CLL thread from last October 2007
<URL:http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/78ef4a5fcd6a1661/e95e42b3307c91cd?#e95e42b3307c91cd>

Which includes message <······························@news.sunsite.dk>
(47164e02$0$90267$14726298 @ news.sunsite.dk)

--
Madhu
From: Albert Krewinkel
Subject: Re: complex inheritance scheme using mixins
Date: 
Message-ID: <fwu63tjmrqd.fsf@pc42.inb.uni-luebeck.de>
Madhu <·······@meer.net> writes:

> [Just an experience report, YMMV]
>
> * Albert Krewinkel <···············@pc06.inb.uni-luebeck.de> :
> Wrote on Thu, 08 May 2008 21:05:07 +0200:
>
> | Now instead of having to write all disk-streams following the above
> | scheme by hand, I'd like to be able to do
> |
> |  (define-cross-product-classes stream-class-graph disk)
>
> I have more than once felt I needed something like this.  However,
> invariably I've ended up abandoning defining classes via ENSURE-CLASS
> mechanisms, preferring instead the explicit DEFCLASS definitions in a
> static file [which the editor can jump to].

Kind of true.  But slime is still helpfull, as it puts me to the
class-generating macro call, which I can expand easily.  That's only one
step more than using nothing but defclass.  You are probably aware of
this, so, uhm, if it's more than personal taste, the reason is hidden
somewhere in the thread.  I will have to examine it closer.

>  The rules of my
> `define-cross-product' equivalent however turned out to be useful to
> both generate the boiler plate which generated the file initially (few
> iterations before settling on class names) and also to write tests to
> check the class structure of the [semi automatically generated] classes.
>
> However in all my cases, the class relations were fairly static, I
> suspect they are in your case too.  For the other case, see
>
> See the CLL thread from last October 2007
> <URL:http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/78ef4a5fcd6a1661/e95e42b3307c91cd?#e95e42b3307c91cd>

Cool, thanks!  I didn't read all what's there yet, but it looks very
much like the stuff I looked for.

Thanks also to Rainer Joswig for pointing out the difference between
multiple inheritance and mixins.  I was sloppy there.