From: Will Hartung
Subject: Creating CLOS classes on the fly.
Date: 
Message-ID: <vfr750EAMBAH.CEo@netcom.com>
I'm banging about on a thing here, and I was confronted with a need to
make a bunch of similiar subclasses to a base class.

MOST of these classes, in all practicality, differ from the base class
only by the values of the slots. Technically, they could easily be
instances of the class. 

On the other hand, several of these subclasses WILL differ from the
base class, specifically on how a few of their slots are calculated
(versus constant, for examples), etc.

So, I decided that I should make all of them subclasses, rather than
just the ones that REALLY needed it. That way I can deal with
everything the same way.

This may be a bad idea, but since I don't really know what I'm doing,
I'm sure that I'll pay for my folly painfully in the end. But, by then
I'll be able to deal with the problem better, being as I'll know at
least one thing NOT to do. Such is the world of programming...

Anyway, I have a list of properties that distinguish the classes.
Specifically, these simple subclasses will basically be the
super-class with a specific default-initargs listed for it.

I can either transform this list of properties, once, into a slew of
(defclass ...) statements, stuff them into a .lsp file, and be done
with it. But it intrigued me that, perhaps, it would be better if I
could just feed the list to a function that would create the classes
for me on app startup. Again, this may be a bad idea...but we've
already been over that.

I've discovered that I can't just wrap (defclass ...) in a loop, being
that it is a macro. The macro expands into ENSURE-CLASS, which doesn't
seem to be mentioned in CLTL2, and the arguments are a little fuzzy in
the ACL-Lite fer Winders documentation.

Hmmm...maybe it's in my "Not SUN TZU'S" Art of MOP book.

I'm just wondering if other folks have done this kind of thing much,
how they've gone about it, etc. Am I going wake up some morning
discovering the not only do I hate myself, but that my program doesn't
respect me anymore? (It was the booze!) It just seems like a nice way
to enable later users to define their own trivial items for use by the
system, at runtime, without actually breaking the whole model into
little "Yeah, but"s and "What if"s all over the place.

Any Learned Wisdom and Sagely advice is welcome, and appreciated.

Thanx!

(Oh, wait -- In order to put this a little bit more on topic for c.l.l --
let me add "...and How can I do this in C++ so it'll be faster?" 8-) )

-- 
Will Hartung - Rancho Santa Margarita. It's a dry heat. ······@netcom.com
1990 VFR750 - VFR=Very Red    "Ho, HaHa, Dodge, Parry, Spin, HA! THRUST!"
1993 Explorer - Cage? Hell, it's a prison.                    -D. Duck

From: Barry Margolin
Subject: Re: Creating CLOS classes on the fly.
Date: 
Message-ID: <5m4c2n$9n9@tools.bbnplanet.com>
In article <················@netcom.com>,
Will Hartung <······@netcom.com> wrote:
>I've discovered that I can't just wrap (defclass ...) in a loop, being
>that it is a macro. The macro expands into ENSURE-CLASS, which doesn't
>seem to be mentioned in CLTL2, and the arguments are a little fuzzy in
>the ACL-Lite fer Winders documentation.
>
>Hmmm...maybe it's in my "Not SUN TZU'S" Art of MOP book.

As you surmised, the "right" way to do this is by using MOP.

However, you can also get around the problem you encountered with DEFCLASS
by calling EVAL.  This is one of the few legitimate uses of EVAL:
constructing calls to macros on the fly and executing them.  So you could
write code like:

(defun make-my-classes (class-info-list)
  (dolist (class-info class-info-list)
    (let ((name (car class-info))
	  (parents (cdr class-info)))
      (eval `(defclass ,name ,parents)))))

(make-my-classes '((c1 p1 p2) (c2 p1 p2 p3) (c3 c2)))
-- 
Barry Margolin, ······@bbnplanet.com
BBN Corporation, Cambridge, MA
(BBN customers, call (800) 632-7638 option 1 for support)
Support the anti-spam movement; see <http://www.cauce.org/>
From: Stefan k. Bamberger
Subject: Re: Creating CLOS classes on the fly.
Date: 
Message-ID: <bambi-2605971117430001@wi6a84.informatik.uni-wuerzburg.de>
In article <··········@tools.bbnplanet.com>, Barry Margolin
<······@bbnplanet.com> wrote:

> In article <················@netcom.com>,
> Will Hartung <······@netcom.com> wrote:
> >I've discovered that I can't just wrap (defclass ...) in a loop, being
> >that it is a macro. The macro expands into ENSURE-CLASS, which doesn't
> >seem to be mentioned in CLTL2, and the arguments are a little fuzzy in
> >the ACL-Lite fer Winders documentation.
> >
> >Hmmm...maybe it's in my "Not SUN TZU'S" Art of MOP book.
> 
> As you surmised, the "right" way to do this is by using MOP.
> 
> However, you can also get around the problem you encountered with DEFCLASS
> by calling EVAL.  This is one of the few legitimate uses of EVAL:
> constructing calls to macros on the fly and executing them.  So you could
> write code like:
> 
> (defun make-my-classes (class-info-list)
>   (dolist (class-info class-info-list)
>     (let ((name (car class-info))
>           (parents (cdr class-info)))
>       (eval `(defclass ,name ,parents)))))
> 
> (make-my-classes '((c1 p1 p2) (c2 p1 p2 p3) (c3 c2)))
> -- 
> Barry Margolin, ······@bbnplanet.com
> BBN Corporation, Cambridge, MA
> (BBN customers, call (800) 632-7638 option 1 for support)
> Support the anti-spam movement; see <http://www.cauce.org/>


I agree with your solution, but unfortunately in ACL for Windows it won't
work. For all CLOS related stuff and EVAL and friends you need the
compiler. In the runtime app, however,  the compiler is thrown off.
The motto is: Avoid recompiling methods (i.e. patching of code !!) or
dynamically defining classes.

I hope ACL/W 4.0 has a solution for this problem so that ACL/W will become
the real language for dynamic programming Franz is always talking about.
;)))

- stefan bamberger
-- 
University of Wuerzburg, GERMANY
·····@informatik.uni-wuerzburg.de
From: Will Hartung
Subject: Re: Creating CLOS classes on the fly.
Date: 
Message-ID: <vfr750EAuzw5.E7F@netcom.com>
·····@informatik.uni-wuerzburg.de (Stefan k. Bamberger) writes:

>In article <··········@tools.bbnplanet.com>, Barry Margolin
><······@bbnplanet.com> wrote:

>> As you surmised, the "right" way to do this is by using MOP.
>> 
>> However, you can also get around the problem you encountered with DEFCLASS
>> by calling EVAL.  This is one of the few legitimate uses of EVAL:
>> constructing calls to macros on the fly and executing them.  So you could
>> write code like:
>> 
>> (defun make-my-classes (class-info-list)
>>   (dolist (class-info class-info-list)
>>     (let ((name (car class-info))
>>           (parents (cdr class-info)))
>>       (eval `(defclass ,name ,parents)))))
>> 
>> (make-my-classes '((c1 p1 p2) (c2 p1 p2 p3) (c3 c2)))
>> -- 
>> Barry Margolin, ······@bbnplanet.com

>I agree with your solution, but unfortunately in ACL for Windows it won't
>work. For all CLOS related stuff and EVAL and friends you need the
>compiler. In the runtime app, however,  the compiler is thrown off.
>The motto is: Avoid recompiling methods (i.e. patching of code !!) or
>dynamically defining classes.

>I hope ACL/W 4.0 has a solution for this problem so that ACL/W will become
>the real language for dynamic programming Franz is always talking about.
>;)))

Yeah, I thought about that too, even though it is effectively a
non-issue for me as ACL-Lite doesn't compile anyway (however,
fasl-write and fasl-read are implemented, and do seem to work for a
number of things).

But, that does bring up an interesting question: Does ACL throw out
the MOP functions when it does generate a run-time? I mean,
technically, it shouldn't be able to throw many of them out, should it?
Hmm..well, I guess everything but make-instance, eh? But if you have
make-instance, everything else (in a REALLY general way) is just
sugar, right? 

Harumph...this MOP can be a dangerous thing...

-- 
Will Hartung - Rancho Santa Margarita. It's a dry heat. ······@netcom.com
1990 VFR750 - VFR=Very Red    "Ho, HaHa, Dodge, Parry, Spin, HA! THRUST!"
1993 Explorer - Cage? Hell, it's a prison.                    -D. Duck
From: Kelly Murray
Subject: Re: Creating CLOS classes on the fly.
Date: 
Message-ID: <5m4qt0$sjc$1@sparky.franz.com>
In article <················@netcom.com>, ······@netcom.com (Will Hartung) writes:
>> MOST of these classes, in all practicality, differ from the base class
>> only by the values of the slots. Technically, they could easily be
>> instances of the class. 
>> 
>> On the other hand, several of these subclasses WILL differ from the
>> base class, specifically on how a few of their slots are calculated
>> (versus constant, for examples), etc.

A simple suggestion is to use a single class.
The different slot initialization can be done outside of CLOS
using simple constructor functions.  
You know know what "kind" of thing you are making, since you must
supply the class name in the make-instance.  So instead of varying
the class name, just vary the creation function name. 
In the most general case, pass the creation name and funcall it

(defclass base ()
  ((same   :initform "constant init")
   (varies :initarg :varies)))

(defun make-base1 ()
  (make-instance 'base :varies "base1"))

(defun make-base2 ()
  (make-instance 'base :varies "base2"))

(defun make-stuff-explicit (arg)
  (cond ((arg-means-make-base1 arg) (make-base1))
	((arg-means-make-base2 arg) (make-base2))
	(t (make-instance 'base) ;; not specific
	  )))
(defun make-stuff-lookup (arg1)
  (funcall (get-base-constructor arg1)))

my $0.02
-kelly edward murray  
From: Will Hartung
Subject: Re: Creating CLOS classes on the fly.
Date: 
Message-ID: <vfr750EAuz4s.CKM@netcom.com>
···@math.ufl.edu (Kelly Murray) writes:

>In article <················@netcom.com>, ······@netcom.com (Will Hartung) writes:
>>> MOST of these classes, in all practicality, differ from the base class
>>> only by the values of the slots. Technically, they could easily be
>>> instances of the class. 
>>> 
>>> On the other hand, several of these subclasses WILL differ from the
>>> base class, specifically on how a few of their slots are calculated
>>> (versus constant, for examples), etc.

>A simple suggestion is to use a single class.
>The different slot initialization can be done outside of CLOS
>using simple constructor functions.  
>You know know what "kind" of thing you are making, since you must
>supply the class name in the make-instance.  So instead of varying
>the class name, just vary the creation function name. 
>In the most general case, pass the creation name and funcall it

>(defclass base ()
>  ((same   :initform "constant init")
>   (varies :initarg :varies)))

>(defun make-base1 ()
>  (make-instance 'base :varies "base1"))

>(defun make-base2 ()
>  (make-instance 'base :varies "base2"))

>(defun make-stuff-explicit (arg)
>  (cond ((arg-means-make-base1 arg) (make-base1))
>	((arg-means-make-base2 arg) (make-base2))
>	(t (make-instance 'base) ;; not specific
>	  )))
>(defun make-stuff-lookup (arg1)
>  (funcall (get-base-constructor arg1)))

Hmmm...*scratch head...ponder*

But, Kelly, this seems to all be ways to work around CLOS. Granted, I
suppose it helps in limiting the number of classes created, but how
much overhead can there be for trivial specializations? Whenever you
see "Look what OOP can do for YOU!" texts, a classic example is to rid
you code of:
    (cond ((this) (do-this)) 
          ((that) (do-that)) 
          (t (do-the-other)))

Code:
    (defclass general-thing ()
        ((x :initarg :x :accessor x)
         (y :initarg :y :accessor y)
         (z :initarg :z :accessor z)))

    (defclass specific-thing1 (general-thing)
        ()
       (:default-initargs :x 1 :y 2 :z 3))

Essentially, I will have a bunch of these. Probably 50-60ish of them,
but roughly 1/3-1/2 will need to have some specialization. Perhaps one
of the slots will be calculated on the fly, versus the constant being
stored.

I guess I can use "general-thing" for everything except those that
need specialization, but I was kinda hoping to use the introspection
ability to keep track of everything. Heck, why keep track of something
twice?

But then again, maybe I'm walking towards the deep end with my cement
golashes on...and you're just throwing me a rope before it is too
late.

-- 
Will Hartung - Rancho Santa Margarita. It's a dry heat. ······@netcom.com
1990 VFR750 - VFR=Very Red    "Ho, HaHa, Dodge, Parry, Spin, HA! THRUST!"
1993 Explorer - Cage? Hell, it's a prison.                    -D. Duck
From: Kelly Murray
Subject: Re: Creating CLOS classes on the fly.
Date: 
Message-ID: <5miads$cju$1@sparky.franz.com>
>>> On the other hand, several of these subclasses WILL differ from the
>>> base class, specifically on how a few of their slots are calculated
>>> (versus constant, for examples), etc.

>>(defun make-base1 ()
>>  (make-instance 'base :varies "base1"))
>>(defun make-base2 ()
>>  (make-instance 'base :varies "base2"))

> But, Kelly, this seems to all be ways to work around CLOS. Granted, I
> suppose it helps in limiting the number of classes created, but how
> much overhead can there be for trivial specializations? Whenever you
> see "Look what OOP can do for YOU!" texts, a classic example is to rid
> you code of:
>    (cond ((this) (do-this)) 
>          ((that) (do-that)) 
>          (t (do-the-other)))

If you have more than one class, where do you put the decision
on which class to create?
There MUST be somewhere in your code something semantically the same as:

   (cond ((this) (make-instance 'this))
         ((that) (make-instance 'that))
         (t (make-instance 'other))


Sixty new classes which only differ from the base by slot initialization is overkill.
A new class has overhead, perhaps not much relative to the instances,
but from what you've described so far, you're paying a price only to get
the use :default-initargs.    It may be the best way to do it.  
That itself is certainly a huge difference from EVAL'ing defclass forms
at runtime, which is an even more hairy solution to what appears to be 
a simple problem.  I just gave my $0.02, (which has now been upped to $.10)
on a simpler solution.  CLOS isn't the solution to everything.  
Common Lisp (which includes CLOS) is the solution to everthing ;)


-kelly edward murray  ···@franz.com