From: tom m
Subject: defining classes on the fly
Date: 
Message-ID: <8f576ee9.0105300047.da349a3@posting.google.com>
Hi,

I'm returning to LISP after several years away and am a bit rusty, so
excuse me if this is an easy one.  I have data which I would like to
use to define CLOS classes on the fly.  The trick is that the name of
the class needs to be dynamically generated.   I ended up doing this:

(eval (generate-class data))

The FAQs and all the books say "If you're using eval, you're not doing
it right".  Is there a better way to do this?

What I'd like to have is a defclass where the first argument is
evaluated.  Is there a way to do this that is portable across CLOS
implementations?

Any help would be appreciated,

--tom

From: Tim Bradshaw
Subject: Re: defining classes on the fly
Date: 
Message-ID: <nkj8zjfrwtr.fsf@tfeb.org>
·······@yahoo.com (tom m) writes:

> 
> What I'd like to have is a defclass where the first argument is
> evaluated.  Is there a way to do this that is portable across CLOS
> implementations?
> 

No.  The function layer underneath CLOS's macro layer is not
standardised.  However it is likely that most or all implementations
support something which is fairly close to the MOP described in the
`Art of The Metaobject Protocol' book, which has a definition of this
layer.  It's usually possible to write code which does things like
this with only some small per-implementation conditionalisation, often
simply making sure you use the right package.

--tim
From: Espen Vestre
Subject: Re: defining classes on the fly
Date: 
Message-ID: <w6vgmjw4ny.fsf@wallace.ws.nextra.no>
·······@yahoo.com (tom m) writes:

> What I'd like to have is a defclass where the first argument is
> evaluated.  Is there a way to do this that is portable across CLOS
> implementations?

not _quite_ portable, but at least portable among implementations that
more or less support the MOP: Use ensure-class!
-- 
  (espen)
From: Raymond Wiker
Subject: Re: defining classes on the fly
Date: 
Message-ID: <861yp7nohc.fsf@raw.grenland.fast.no>
·······@yahoo.com (tom m) writes:

> Hi,
> 
> I'm returning to LISP after several years away and am a bit rusty, so
> excuse me if this is an easy one.  I have data which I would like to
> use to define CLOS classes on the fly.  The trick is that the name of
> the class needs to be dynamically generated.   I ended up doing this:
> 
> (eval (generate-class data))
> 
> The FAQs and all the books say "If you're using eval, you're not doing
> it right".  Is there a better way to do this?
> 
> What I'd like to have is a defclass where the first argument is
> evaluated.  Is there a way to do this that is portable across CLOS
> implementations?
> 
> Any help would be appreciated,

(defmacro genclass (class-id)
  (let ((class-name (intern 
		     (concatenate 'string 
				  "GENERATED-CLASS-"
				  (string-upcase class-id)))))
    `(defclass ,class-name () ())))

(genclass "my-class")

-- 
Raymond Wiker
·············@fast.no
From: Tim Bradshaw
Subject: Re: defining classes on the fly
Date: 
Message-ID: <nkjg0dnt813.fsf@tfeb.org>
Raymond Wiker <·············@fast.no> writes:

> 
> (defmacro genclass (class-id)
>   (let ((class-name (intern 
> 		     (concatenate 'string 
> 				  "GENERATED-CLASS-"
> 				  (string-upcase class-id)))))
>     `(defclass ,class-name () ())))
> 
> (genclass "my-class")
> 

(let ((x "class-name"))
  (genclass x))

Doesn't work.
From: Raymond Wiker
Subject: Re: defining classes on the fly
Date: 
Message-ID: <86wv6zm6ad.fsf@raw.grenland.fast.no>
Tim Bradshaw <···@tfeb.org> writes:

> Raymond Wiker <·············@fast.no> writes:
> 
> > 
> > (defmacro genclass (class-id)
> >   (let ((class-name (intern 
> > 		     (concatenate 'string 
> > 				  "GENERATED-CLASS-"
> > 				  (string-upcase class-id)))))
> >     `(defclass ,class-name () ())))
> > 
> > (genclass "my-class")
> > 
> 
> (let ((x "class-name"))
>   (genclass x))

(defmacro genclass (class-id)
  (let ((class-id-var (gensym))
	(class-name-var (gensym)))
    `(let* ((,class-id-var ,class-id)
	    (,class-name-var 
	     (intern (concatenate 'string 
				  "GENERATED-CLASS-"
				  (string-upcase ,class-id-var)))))
      `(defclass ,,class-name-var () ()))))

        --- which no doubt isn't perfect, either.

        //Raymond.

-- 
Raymond Wiker
·············@fast.no
From: Tim Bradshaw
Subject: Re: defining classes on the fly
Date: 
Message-ID: <nkjd78rt4p0.fsf@tfeb.org>
Raymond Wiker <·············@fast.no> writes:

> (defmacro genclass (class-id)
>   (let ((class-id-var (gensym))
> 	(class-name-var (gensym)))
>     `(let* ((,class-id-var ,class-id)
> 	    (,class-name-var 
> 	     (intern (concatenate 'string 
> 				  "GENERATED-CLASS-"
> 				  (string-upcase ,class-id-var)))))
>       `(defclass ,,class-name-var () ()))))
> 
>         --- which no doubt isn't perfect, either.
> 

No, it's not (:-).  This returns a list beginning DEFCLASS, which you
then need to give to EVAL.

The point is that if you have only a macro that does not evaluate some
argument, then you can't easily synthesize from it a function that
does.  You probably *can* do so by recursively macroexpanding and
using a code-walker to find the functional layer if there is one.

This is why a lot of Lisp designs are what I call `two layered' - they
have a convenient macro layer, and an underlying function-based layer
which you can use for your own macros or to provide things that the
macro layer can't, like computed class names here.

(`two layered' may be a standard term I picked up somewhere).

--tim
From: Frode Vatvedt Fjeld
Subject: Re: defining classes on the fly
Date: 
Message-ID: <2h4ru39fv1.fsf@dslab7.cs.uit.no>
·······@yahoo.com (tom m) writes:

> (eval (generate-class data))
> 
> The FAQs and all the books say "If you're using eval, you're not
> doing it right".  Is there a better way to do this?

"Not doing it right" can mean one other thing than what you're
asking. That is, do you _really_ need to run DEFCLASS at run-time?

Often when one needs to have such declarations (DEFCLASS etc.)
generated from some data, that mapping preferrably occurs at
compile-time. Think of it as compiling that "some data" into a lisp
program. This is typically done by having macros that transforms the
data to DEFCLASS forms.

However, when the data changes over (run-) time, that approach won't
work. But in such cases the solution can be to declare classes that
are able to model the changing data. Sometimes, this can be viewed as
creating your own specialized type-system, using compile-time DEFCLASS
classes for the meta-hierarchy.

I can't really think of any situation where defining new classes at
run-time would be justified (I'm sure they exist, though).

-- 
Frode Vatvedt Fjeld
From: Espen Vestre
Subject: Re: defining classes on the fly
Date: 
Message-ID: <w68zjfuh6x.fsf@wallace.ws.nextra.no>
Frode Vatvedt Fjeld <······@acm.org> writes:

> I can't really think of any situation where defining new classes at
> run-time would be justified (I'm sure they exist, though).

This reminds me of famous remarks about 640K computer memory (etc.) ;-)

The system I'm mainly working on, defines hundreds of new classes
at run time, all the time. The information needed to generate the 
classes comes from tables in a relational DB. It's all done with
ENSURE-CLASS, which is a relatively standard thing (since it's one
of the simpler parts of AMOP). 
-- 
  (espen)
From: tom m
Subject: Re: defining classes on the fly
Date: 
Message-ID: <8f576ee9.0105302042.5f66f0c3@posting.google.com>
Espen Vestre <·····@*do-not-spam-me*.vestre.net> wrote in message news:<··············@wallace.ws.nextra.no>...

> The system I'm mainly working on, defines hundreds of new classes
> at run time, all the time. The information needed to generate the 
> classes comes from tables in a relational DB. It's all done with
> ENSURE-CLASS, which is a relatively standard thing (since it's one
> of the simpler parts of AMOP).

This is very similar to my application which is being driven off a
relational database, as well.  On a more philosophical note, why is
using ensure-class better than eval?

--tom
From: Kent M Pitman
Subject: Re: defining classes on the fly
Date: 
Message-ID: <sfw3d9m84h6.fsf@world.std.com>
·······@yahoo.com (tom m) writes:

> On a more philosophical note, why is using ensure-class better than
> eval?

I'm not sure the context of this, but in general ANYTHING that works
is better than using EVAL.  When it comes to a tree-shaking gc for
delivery, there are certain opaque operators which hide the entire 
language behind them.  Depending on the arg to EVAL, anything might be
needed so nothing can be easily removed from the implementation at
application dump time.  By contrast, the facilities entailed by a use
of ENSURE-CLASS might be possible to be fewer, I haven't thought about
it; certainly they can't be any worse.  (Actually, different people 
attribute the blame differently.  Some might say it's not EVAL but INTERN
that is the bad guy.  But you get the idea...)
From: Espen Vestre
Subject: Re: defining classes on the fly
Date: 
Message-ID: <w67kyxahfv.fsf@wallace.ws.nextra.no>
·······@yahoo.com (tom m) writes:

> This is very similar to my application which is being driven off a
> relational database, as well.  On a more philosophical note, why is
> using ensure-class better than eval?

in addition to the general "eval is evil" answer, an additional
explanation is that eval means using _more_ machinery, whereas
ensure-class means using _less_ machinery (a possible implementation
of defclass could expand it into something involving ensure-class,
e.g. lispworks seem to expand it into something involving a variant of
ensure-class).
-- 
  (espen)
From: Steven M. Haflich
Subject: Re: defining classes on the fly
Date: 
Message-ID: <3B177616.27D02297@pacbell.net>
tom m wrote:

> This is very similar to my application which is being driven off a
> relational database, as well.  On a more philosophical note, why is
> using ensure-class better than eval?

I'm going to respond with an argument rather different than the
practical details mentioned by the two other respondents I have read
so far.  Consider the semantics of CL:

In a very strong sense, the only thing a CL implementation can do is
to apply a function to arguments.  The function EVAL is a function;
macros are expanded by applying the MACRO-FUNCTION to the form; etc.
The existence of an evaluator is _not_ something defined by the
underlying primitive semantics of the language.  Rather, EVAL is
either a very complex function in an implementation that "has an
evaluator", and something like

  (defun eval (form) (funcall (compile nil form)))

in implementations that don't.

Now, given that forms aren't executable until converted into functions,
there are only three ways in CL that a random form can be transformed
into a function:

  (COMPILE () '(LAMBDA ...))
  (COERCE '(LAMBDA ...) 'FUNCTION)
  COMPILE-FILE then LOAD

I'm purposely here ignoring the possibility of

  (EVAL `(FUNCTION (LAMBDA () ,FORM)))

since the defined semantics of EVAL can be implemented in terms of
COMPILE, as shown above.

Now, in earlier Lisps the distinction between forms and functions was
rather looser.  For instance, it used to be possible to pass as a
functional argument a Lisp object of type CONS whose car happened to
be LAMBDA and whose cadr etc. etc. etc., i.e., a lambda expression.
X3J13 tightened this up (even though implementations may still support
this usage in interest of back compatibility) and properly so, because
in _modern_ lexically-scoped Lisp dialects the lexical context of a
function is important, and a lambda expression doesn't capture its
lexical environment until it is "promoted" to a function by one of the
three mechanisms cited above.

The separation of functions frmo lambda expressions was IMO a very
important result of the X3J13 standardization process.  Some of the
actions that brought about this separation were written in ways that
depended on particular details (such as the potential inconsistency
between the important class FUNCTION and the set of objects that
might reasonably be passed as a functional argument -- ultimately
settled with a compromise including the class SYMBOL) but the
eventual clarification makes the language much more tractible to
automated analysis by compilers and other code-understanding tools.
Further, it kept the language competitive and up-to-date with regard
other "cleaner" and more "modern" languages like Scheme where
(paradoxically) SFAIK there are no standard ways at runtime to cross
that boundary between lambda expression and function.

So, that's why it's cleaner at runtime to use functions like
ENSURE-CLASS instead of passing a defining macro to EVAL.
From: Reini Urban
Subject: Re: defining classes on the fly
Date: 
Message-ID: <9f5efc$700$2@fstgss02.tu-graz.ac.at>
Frode Vatvedt Fjeld <······@acm.org> wrote:
: I can't really think of any situation where defining new classes at
: run-time would be justified (I'm sure they exist, though).

I'm doing this to import COM objects at run-time. (Corman Lisp)

It's a speed penalty, but pays it back with all the nice CLOS 
possibilities I wouldn't have with a plain normal COM/FFI approach.

Compile-time importing via TLB's can only be done for early boundable COM
servers, but a lot of stuff is only defined via run-time queryable 
IDispatch without a proper TLB.
-- 
Reini Urban
http://xarch.tu-graz.ac.at/acadwiki/AutoLispFaq
From: Frode Vatvedt Fjeld
Subject: Re: defining classes on the fly
Date: 
Message-ID: <2hu2217je5.fsf@dslab7.cs.uit.no>
Reini Urban <······@x-ray.at> writes:

> Frode Vatvedt Fjeld <······@acm.org> wrote:
> : I can't really think of any situation where defining new classes at
> : run-time would be justified (I'm sure they exist, though).
> 
> I'm doing this to import COM objects at run-time. (Corman Lisp)
> 
> It's a speed penalty, but pays it back with all the nice CLOS
> possibilities I wouldn't have with a plain normal COM/FFI approach.

What kind of possibilities, exactly? I mean, defining the classes
run-time I suppose you don't have specialized methods, unless you also
define those run-time.

Just curious,
-- 
Frode Vatvedt Fjeld
From: Espen Vestre
Subject: Re: defining classes on the fly
Date: 
Message-ID: <w6r8x57iri.fsf@wallace.ws.nextra.no>
Frode Vatvedt Fjeld <······@acm.org> writes:

> What kind of possibilities, exactly? I mean, defining the classes
> run-time I suppose you don't have specialized methods, unless you also
> define those run-time.

we mainly use it for multiple inheritance of slots (and their default
values).
-- 
  (espen)
From: Reini Urban
Subject: Re: defining classes on the fly
Date: 
Message-ID: <9fcuim$3or$2@fstgss02.tu-graz.ac.at>
Frode Vatvedt Fjeld <······@acm.org> wrote:
: Reini Urban <······@x-ray.at> writes:
:> Frode Vatvedt Fjeld <······@acm.org> wrote:
:> : I can't really think of any situation where defining new classes at
:> : run-time would be justified (I'm sure they exist, though).
:> 
:> I'm doing this to import COM objects at run-time. (Corman Lisp)
:> 
:> It's a speed penalty, but pays it back with all the nice CLOS
:> possibilities I wouldn't have with a plain normal COM/FFI approach.

: What kind of possibilities, exactly? I mean, defining the classes
: run-time I suppose you don't have specialized methods, unless you also
: define those run-time.

I'm defining the whole object tree at run-time. classes with slots for the
properties (the hardest part is to get the inheritance tree from COM),
their methods (related to COM-style get-prop set-prop invoke), and global 
constants and enumerations.

But it doesn't work yet as it should. 
-- 
Reini Urban
http://xarch.tu-graz.ac.at/acadwiki/AutoLispFaq