From: Edi Weitz
Subject: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <umzunvzjk.fsf@agharta.de>
I've used the MOP to create a new meta class which provides some new
slot options.  Let's suppose the new meta class is called FOO-CLASS
and one of the new slot options is called FOO-DEFAULTS.  If I now have
a DEFCLASS form like this

  (defclass foo ()
      ((x :name x
          :initarg :x
          :accessor x
          :foo-defaults *global-defaults*))
    (:metaclass foo-class))

then the value of the slot FOO-DEFAULTS of the slot definition object
for X is the /symbol/ *GLOBAL-DEFAULTS*.  However, I want this to be
evaluated, i.e. I wanted the /value/ of (the special variable)
*GLOBAL-DEFAULTS* instead.  In other words, I want the slot option
FOO-DEFAULTS to behave like INITFORM which behind the scenes creates
an INITFUNCTION in the right lexical environment.

My current understanding is that this behaviour of the INITFORM slot
is hard-coded into DEFCLASS[1] and I have to write my own MY-DEFCLASS
macro to get what I want.  Is that correct or can I achieve the
desired behaviour with the MOP alone?

Thanks,
Edi.

[1] Tracing ENSURE-CLASS reveals that when ENSURE-CLASS is called, the
    init function for the INITFORM slot has already been created - and
    that corresponds with my understanding of the relevant section in
    the AMOP book.  I think the call to ENSURE-CLASS-USING-CLASS is
    the first time I can intervene and, anyway, the lexical
    environment is already lost at this point.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")

From: Arthur Lemmens
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <opslkp3si1k6vmsw@news.xs4all.nl>
Edi Weitz wrote:

> I've used the MOP to create a new meta class which provides some new
> slot options.  Let's suppose the new meta class is called FOO-CLASS
> and one of the new slot options is called FOO-DEFAULTS.  If I now have
> a DEFCLASS form like this
>
> (defclass foo ()
> ((x :name x
> :initarg :x
> :accessor x
> :foo-defaults *global-defaults*))
> (:metaclass foo-class))
>
> then the value of the slot FOO-DEFAULTS of the slot definition object
> for X is the /symbol/ *GLOBAL-DEFAULTS*.  However, I want this to be
> evaluated, i.e. I wanted the /value/ of (the special variable)
> *GLOBAL-DEFAULTS* instead.

In Lispworks, you can control this by writing a method for
CLOS:PROCESS-A-SLOT-OPTION.  There are some examples in the
manual.

In general, I don't think there is a portable solution for this with
the MOP.  But I'm no MOP expert.

Arthur
From: Edi Weitz
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <ubrb26hu2.fsf@agharta.de>
On Wed, 02 Feb 2005 15:20:06 +0100, Arthur Lemmens <········@xs4all.nl> wrote:

> In Lispworks, you can control this by writing a method for
> CLOS:PROCESS-A-SLOT-OPTION.  There are some examples in the manual.
>
> In general, I don't think there is a portable solution for this with
> the MOP.  But I'm no MOP expert.

Thanks to you and Pascal.  Nice that they thought of providing this
interface... :)

Cheers,
Edi.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: ·······@gmail.com
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <1107364080.962705.248820@z14g2000cwz.googlegroups.com>
I'm a little curious about this myself.  Is your goal to have the
evaluation of *global-defaults* done at make-instance time, rather than
at the time of the defclass?  I ask because I have designed a metaclass
allowing me to specify a list of allowable values for a given slot.  I
simply evaluated the :allowed-slots form within
compute-effective-slot-definition-initargs.  I'm assuming this behavior
is not what you're looking for.

Since you use Lispworks, this is somewhat a non-issue for you.  I'm
just curious, since I didn't consider that someone might want behavior
like I believe you are describing.

Anthony W. Juckel
From: Edi Weitz
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <uoef23cjw.fsf@agharta.de>
On 2 Feb 2005 09:08:00 -0800, ········@gmail.com" <·······@gmail.com> wrote:

> I'm a little curious about this myself.  Is your goal to have the
> evaluation of *global-defaults* done at make-instance time, rather
> than at the time of the defclass?

At the moment it'd be OK if it happened when the DEFCLASS form is
macro-expanded.  But when I saw it I began to wonder how one would do
this in the general case.

> I ask because I have designed a metaclass allowing me to specify a
> list of allowable values for a given slot.  I simply evaluated the
> :allowed-slots form within
> compute-effective-slot-definition-initargs.  I'm assuming this
> behavior is not what you're looking for.

You mean COMPUTE-EFFECTIVE-SLOT-DEFINITION?  But you're using EVAL,
right, so you're losing the lexical environment?  Again, for my
current problem this doesn't matter but it made me wonder how to do
this.

> Since you use Lispworks, this is somewhat a non-issue for you.

I'm a LispWorks user but the application I'm working on uses
AllegroCL... :)

> I'm just curious, since I didn't consider that someone might want
> behavior like I believe you are describing.

Why do you think so?

Cheers,
Edi.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: ·······@gmail.com
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <1107378812.198589.230810@l41g2000cwc.googlegroups.com>
Edi Weitz wrote:
> You mean COMPUTE-EFFECTIVE-SLOT-DEFINITION?  But you're using EVAL,
> right, so you're losing the lexical environment?  Again, for my
> current problem this doesn't matter but it made me wonder how to do
> this.
>

No, I meant COMPUTE-EFFECTIVE-SLOT-DEFINITION-INITARGS.  Doing a quick
Google search, I'm inclided to believe that might not be included in
AMOP (I'll check for sure when I get home), in which case my solution
was no more "standard" than that proposed by Arthur and Pascal.  I've
only tested my implementation on SBCL and CMUCL, though a quick peek at
prompt.franz.com indicates that allegro has a similar function.

Additionally, Kevin Rosenberg had this to say
(http://www.caddr.com/macho/archives/sbcl-devel/2003-3/1666.html):

"Lispworks, AllegroCL, CMUCL/SCL all have this function, but none of
them export it. The interface is the same for all of these
implementations except for Lispworks which takes an additional
argument compared to the other implementations.  Perhaps it is too
low-level and implementation dependent to export."

> > I'm just curious, since I didn't consider that someone might want
> > behavior like I believe you are describing.
>
> Why do you think so?
>

It's not so much an active belief that such behavior (having a slot
option access the lexical environment) wouldn't be required, but rather
an oversight on my part.  I simply hadn't considered it.  Now that I
have, I find myself wanting to find a way to make that work as well.
Also, although I'm not a very experienced lisper, I still find that
eval leaves a bad taste in my mouth, so it would be good to find a
better solution.

Anthony W. Juckel
From: Edi Weitz
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <ubrb23b8i.fsf@agharta.de>
On 2 Feb 2005 13:13:32 -0800, ········@gmail.com" <·······@gmail.com> wrote:

> (http://www.caddr.com/macho/archives/sbcl-devel/2003-3/1666.html):

Thanks for the info - didn't know about that one.  If AllegroCL, LW,
CMUCL, and SBCL have it then it's portable enough for me... :)

However, it's a function that's called after DEFCLASS has been
expanded so it doesn't solve my original problem.

Cheers,
Edi.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Kenny Tilton
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <fhdMd.78019$ld2.26278945@twister.nyc.rr.com>
Edi Weitz wrote:

> On 2 Feb 2005 13:13:32 -0800, ········@gmail.com" <·······@gmail.com> wrote:
> 
> 
>>(http://www.caddr.com/macho/archives/sbcl-devel/2003-3/1666.html):
> 
> 
> Thanks for the info - didn't know about that one.  If AllegroCL, LW,
> CMUCL, and SBCL have it then it's portable enough for me... :)
> 
> However, it's a function that's called after DEFCLASS has been
> expanded so it doesn't solve my original problem.

I normally save this for newbies, not Certifed Lisp Gods, but... what do 
you need this for? Jeez, inheritance is not finalized until 
make-instance time. ie, Are you sure you are still programming Common Lisp?

Mind you, you have me wondering how even ":initform *zzzz*" behaves, so 
I am no expert on using specials in such circumstances. I just wonder if 
Bad Things will not start happening if you get impatient and try to do 
too much (runtime stuff) at defclass time. There's a reason you get the 
symbol, methinks.

kt

-- 
Cells? Cello? Cells-Gtk?: http://www.common-lisp.net/project/cells/
Why Lisp? http://lisp.tech.coop/RtL%20Highlight%20Film

"Doctor, I wrestled with reality for forty years, and I am happy to 
state that I finally won out over it." -- Elwood P. Dowd
From: Edi Weitz
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <uu0ouwkoh.fsf@agharta.de>
On Wed, 02 Feb 2005 23:22:51 GMT, Kenny Tilton <·······@nyc.rr.com> wrote:

> I normally save this for newbies, not Certifed Lisp Gods, but...

Hehe... :)

> what do you need this for? Jeez, inheritance is not finalized until
> make-instance time. ie, Are you sure you are still programming
> Common Lisp?

As I said in my other message I don't actually need it (yet).  I just
stumbled across it and thought that it's a small wart in the MOP
design.  Those guys knew that the :INITFORM slot option and the
:DEFAULT-INITARGS needed special treatment but it didn't occur to them
that someone using the MOP might want similar behaviour for his /own/
slot or class options.

But, hey, who am I do criticize them?

> Mind you, you have me wondering how even ":initform *zzzz*" behaves,

  CL-USER> (defparameter *foo* 42)
  *FOO*
  CL-USER> (defclass foo ()
             ((x :initform *foo*)))
  #<STANDARD-CLASS FOO>
  CL-USER> (defparameter *fn* (mop:slot-definition-initfunction
                               (first
                                (mop:compute-slots
                                 (find-class 'foo)))))
  *FN*
  CL-USER> (funcall *fn*)
  42
  CL-USER> (incf *foo*)
  43
  CL-USER> (funcall *fn*)
  43
  CL-USER> (let ((counter 1))
             (defclass foo ()
               ((x :initform counter)))
             (defun up-it ()
               (incf counter)))
  UP-IT
  CL-USER> (setq *fn* (mop:slot-definition-initfunction
                       (first
                        (mop:compute-slots
                         (find-class 'foo)))))
  #<Interpreted Closure (ACLMOP:SLOT-DEFINITION-INITFUNCTION FOO X) @
    #x721ebdc2>
  CL-USER> (funcall *fn*)
  1
  CL-USER> (up-it)
  2
  CL-USER> (funcall *fn*)
  2

See?  Behind the scenes DEFCLASS has created an INITFUNCTION slot for
the slot definition object which is a function without any arguments
that captures the lexical environment of the DEFCLASS form.  Each time
the init form is needed this function is actually invoked.  Obviously,
you can only define this function at DEFCLASS macro expansion time.

> so I am no expert on using specials in such circumstances. I just
> wonder if Bad Things will not start happening if you get impatient
> and try to do too much (runtime stuff) at defclass time. There's a
> reason you get the symbol, methinks.

I think it's just an oversight.  There's a reason the LispWorks folks
exported PROCESS-A-SLOT... :)

Cheers,
Edi.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Kenny Tilton
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <qkkMd.101345$kq2.93724@twister.nyc.rr.com>
Edi Weitz wrote:

> On Wed, 02 Feb 2005 23:22:51 GMT, Kenny Tilton <·······@nyc.rr.com> wrote:
> 
> 
>>I normally save this for newbies, not Certifed Lisp Gods, but...
> 
> 
> Hehe... :)
> 
> 
>>what do you need this for? Jeez, inheritance is not finalized until
>>make-instance time. ie, Are you sure you are still programming
>>Common Lisp?
> 
> 
> As I said in my other message I don't actually need it (yet).  I just
> stumbled across it and thought that it's a small wart in the MOP
> design.  Those guys knew that the :INITFORM slot option and the
> :DEFAULT-INITARGS needed special treatment but it didn't occur to them
> that someone using the MOP might want similar behaviour for his /own/
> slot or class options.
> 
> But, hey, who am I do criticize them?
> 
> 
>>Mind you, you have me wondering how even ":initform *zzzz*" behaves,
> 
> 
>   CL-USER> (defparameter *foo* 42)
>   *FOO*
>   CL-USER> (defclass foo ()
>              ((x :initform *foo*)))
>   #<STANDARD-CLASS FOO>
>   CL-USER> (defparameter *fn* (mop:slot-definition-initfunction
>                                (first
>                                 (mop:compute-slots
>                                  (find-class 'foo)))))
>   *FN*
>   CL-USER> (funcall *fn*)
>   42
>   CL-USER> (incf *foo*)
>   43
>   CL-USER> (funcall *fn*)
>   43
>   CL-USER> (let ((counter 1))
>              (defclass foo ()
>                ((x :initform counter)))
>              (defun up-it ()
>                (incf counter)))
>   UP-IT
>   CL-USER> (setq *fn* (mop:slot-definition-initfunction
>                        (first
>                         (mop:compute-slots
>                          (find-class 'foo)))))
>   #<Interpreted Closure (ACLMOP:SLOT-DEFINITION-INITFUNCTION FOO X) @
>     #x721ebdc2>
>   CL-USER> (funcall *fn*)
>   1
>   CL-USER> (up-it)
>   2
>   CL-USER> (funcall *fn*)
>   2
> 
> See?  Behind the scenes DEFCLASS has created an INITFUNCTION slot for
> the slot definition object which is a function without any arguments
> that captures the lexical environment of the DEFCLASS form.  Each time
> the init form is needed this function is actually invoked.  Obviously,
> you can only define this function at DEFCLASS macro expansion time.

No see. What if we had:

(defparameter *foo* 42)

(defclass foo ()
   ((x :initform *foo* :edi-form *foo*)))

(let ((*foo* 2001))
    (describe (make-instance 'foo)))

Sounds like you want x=2001 and edi-form=42, but instead at shared-init 
time all you have is the symbol '*foo*. You have to take the 
symbol-value to get a value, and it will not be the value current at 
defclass time. But that is non-deterministic anyway, since it is bound 
up with load time. That just runs away from the source quality of 
defclass, which in the other cases you cite stays lexical. (The 
initfunction created dynamically at defclass time has lexical scope and 
does not get invoked until shared-init time or some Lisp God hacks the 
internals and invokes it directly, which just confuses things--the scope 
is still lexical.)

IIUC, you want the compilation of a defclass form to be shaped by a 
dynamic binding obtaining at compile time. Would you expect:

(defparameter *answer* 42)

(defun uni-meaning () *answer*)

...always to return 42?

kt

-- 
Cells? Cello? Cells-Gtk?: http://www.common-lisp.net/project/cells/
Why Lisp? http://lisp.tech.coop/RtL%20Highlight%20Film

"Doctor, I wrestled with reality for forty years, and I am happy to 
state that I finally won out over it." -- Elwood P. Dowd
From: Edi Weitz
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <uis5ahxx3.fsf@agharta.de>
On Thu, 03 Feb 2005 07:24:06 GMT, Kenny Tilton <·······@nyc.rr.com> wrote:

> No see. What if we had:
>
> (defparameter *foo* 42)
>
> (defclass foo ()
>    ((x :initform *foo* :edi-form *foo*)))
>
> (let ((*foo* 2001))
>     (describe (make-instance 'foo)))
>
> Sounds like you want x=2001 and edi-form=42, but instead at
> shared-init time all you have is the symbol '*foo*. You have to take
> the symbol-value to get a value, and it will not be the value
> current at defclass time. But that is non-deterministic anyway,
> since it is bound up with load time. That just runs away from the
> source quality of defclass, which in the other cases you cite stays
> lexical. (The initfunction created dynamically at defclass time has
> lexical scope and does not get invoked until shared-init time or
> some Lisp God hacks the internals and invokes it directly, which
> just confuses things--the scope is still lexical.)
>
> IIUC, you want the compilation of a defclass form to be shaped by a
> dynamic binding obtaining at compile time. Would you expect:
>
> (defparameter *answer* 42)
>
> (defun uni-meaning () *answer*)
>
> ...always to return 42?

No, of course not.  Only my second example was to demonstrate the
capturing of the lexical environment, not the first one.  If I use
special variables I want special behaviour... :)

The cool thing with :INITFORM is that you can force it to behave like
you want: Use a constant value always, use a function, always rely on
the same lexical environment, or use a special variable so you can
easily change the value at run time.  Hey, it's Common Lisp...

I wouldn't call this behaviour non-deterministic - it's clearly
described in the CLHS DEFCLASS dictionary entry.  I want that for my
own slots, too!!!

:)

Cheers,
Edi.

PS: The fact I was originally complaining about, BTW, was not that I
    got the value of the special variable at DEFCLASS expansion time
    but that I only got its symbol, so I was forced to use ugly EVAL.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Marc Battyani
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <ctsrda$bcf@library2.airnews.net>
"Edi Weitz" <········@agharta.de> wrote

> PS: The fact I was originally complaining about, BTW, was not that I
>     got the value of the special variable at DEFCLASS expansion time
>     but that I only got its symbol, so I was forced to use ugly EVAL.

Why don't you shadow defclass to wrap your slot option form into a lambda ?
That's what I do.

Marc
From: Edi Weitz
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <uzmymkm95.fsf@agharta.de>
On Thu, 3 Feb 2005 10:37:58 +0100, "Marc Battyani" <·············@fractalconcept.com> wrote:

> Why don't you shadow defclass to wrap your slot option form into a
> lambda ?  That's what I do.

Yeah, that's what I wrote in my original message.  I just wanted to
know if there's a MOP way to do it.

Cheers,
Edi.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Edi Weitz
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <uk6pq3cbf.fsf@agharta.de>
On Wed, 02 Feb 2005 21:57:23 +0100, Edi Weitz <········@agharta.de> wrote:

> On 2 Feb 2005 09:08:00 -0800, ········@gmail.com" <·······@gmail.com> wrote:
>
>> I'm just curious, since I didn't consider that someone might want
>> behavior like I believe you are describing.
>
> Why do you think so?

Forget this last question - I mis-read your sentence above.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Pascal Costanza
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <ctqob5$s3f$1@snic.vub.ac.be>
Edi Weitz wrote:
> I've used the MOP to create a new meta class which provides some new
> slot options.  Let's suppose the new meta class is called FOO-CLASS
> and one of the new slot options is called FOO-DEFAULTS.  If I now have
> a DEFCLASS form like this
> 
>   (defclass foo ()
>       ((x :name x
>           :initarg :x
>           :accessor x
>           :foo-defaults *global-defaults*))
>     (:metaclass foo-class))
> 
> then the value of the slot FOO-DEFAULTS of the slot definition object
> for X is the /symbol/ *GLOBAL-DEFAULTS*.  However, I want this to be
> evaluated, i.e. I wanted the /value/ of (the special variable)
> *GLOBAL-DEFAULTS* instead.  In other words, I want the slot option
> FOO-DEFAULTS to behave like INITFORM which behind the scenes creates
> an INITFUNCTION in the right lexical environment.
> 
> My current understanding is that this behaviour of the INITFORM slot
> is hard-coded into DEFCLASS[1] and I have to write my own MY-DEFCLASS
> macro to get what I want.  Is that correct or can I achieve the
> desired behaviour with the MOP alone?

I think you are correct. LispWorks provides the generic functions 
process-a-slot-option and process-a-class-option that may help.


Pascal
From: Edi Weitz
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <ur7jvnfjz.fsf@agharta.de>
On Wed, 02 Feb 2005 14:53:19 +0100, Edi Weitz <········@agharta.de> wrote:

> I've used the MOP to create a new meta class which provides some new
> slot options.  Let's suppose the new meta class is called FOO-CLASS
> and one of the new slot options is called FOO-DEFAULTS.  If I now
> have a DEFCLASS form like this
>
>   (defclass foo ()
>       ((x :name x
>           :initarg :x
>           :accessor x
>           :foo-defaults *global-defaults*))
>     (:metaclass foo-class))
>
> then the value of the slot FOO-DEFAULTS of the slot definition
> object for X is the /symbol/ *GLOBAL-DEFAULTS*.  However, I want
> this to be evaluated, i.e. I wanted the /value/ of (the special
> variable) *GLOBAL-DEFAULTS* instead.  In other words, I want the
> slot option FOO-DEFAULTS to behave like INITFORM which behind the
> scenes creates an INITFUNCTION in the right lexical environment.
>
> My current understanding is that this behaviour of the INITFORM slot
> is hard-coded into DEFCLASS and I have to write my own MY-DEFCLASS
> macro to get what I want.  Is that correct or can I achieve the
> desired behaviour with the MOP alone?

Duane Rettig asked me to summarize what came out of this discussion in
order to provide a reference for people reading this later on, so
there you are:

Pascal and Arthur showed a LispWorks-specific extension and other
Lisps probably have similar offerings but there's no way you can
achieve what I wanted with the MOP alone in a portable way (provided
you consider the MOP itself to be portable).

As I suggested above and Marc has also pointed out you can write your
own MY-DEFCLASS (you can of course even shadow CL:DEFCLASS and give
the same name to your macro) which could look like this (untested):

  (defmacro my-defclass (class-name
                         superclasses
                         (&rest slot-specifiers)
                         &rest class-options)
    (let ((metaclass (second (find :metaclass class-options
                                   :key #'first
                                   :test #'eq))))
      (cond ((eq metaclass 'foo-class)
             (let* ((new-slot-specifiers
                     (loop for slot-specifier in slot-specifiers
                           when (and (listp slot-specifier)
                                     (getf (rest slot-specifier) :foo-defaults))
                             collect (append slot-specifier
                                             (list :foo-defaults-closure
                                                   `#'(lambda ()
                                                        ,(getf (rest slot-specifier)
                                                               :foo-defaults))))
                           else collect slot-specifier)))
               `(cl:defclass ,class-name ,superclasses
                     (,@new-slot-specifiers)
                  ,@class-options)))
            (t `(cl:defclass ,class-name ,superclasses
                     (,@slot-specifiers)
                  ,@class-options)))))

This is portable and will leave the form in :FOO-DEFAULTS as is and
add another slot :FOO-DEFAULTS-CLOSURE with a closure that properly
captures the lexical environment.

The downside is that this macro doesn't play well with other macros
that shadow CL:DEFCLASS themselves like, say, CLSQL's DEF-VIEW-CLASS.
Looks like you can't have the cake and eat it, too.

Cheers,
Edi.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Duane Rettig
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <4zmyi6gtg.fsf@franz.com>
Edi Weitz <········@agharta.de> writes:

> On Wed, 02 Feb 2005 14:53:19 +0100, Edi Weitz <········@agharta.de> wrote:
> 
> > I've used the MOP to create a new meta class which provides some new
> > slot options.  Let's suppose the new meta class is called FOO-CLASS
> > and one of the new slot options is called FOO-DEFAULTS.  If I now
> > have a DEFCLASS form like this
> >
> >   (defclass foo ()
> >       ((x :name x
> >           :initarg :x
> >           :accessor x
> >           :foo-defaults *global-defaults*))
> >     (:metaclass foo-class))
> >
> > then the value of the slot FOO-DEFAULTS of the slot definition
> > object for X is the /symbol/ *GLOBAL-DEFAULTS*.  However, I want
> > this to be evaluated, i.e. I wanted the /value/ of (the special
> > variable) *GLOBAL-DEFAULTS* instead.  In other words, I want the
> > slot option FOO-DEFAULTS to behave like INITFORM which behind the
> > scenes creates an INITFUNCTION in the right lexical environment.
> >
> > My current understanding is that this behaviour of the INITFORM slot
> > is hard-coded into DEFCLASS and I have to write my own MY-DEFCLASS
> > macro to get what I want.  Is that correct or can I achieve the
> > desired behaviour with the MOP alone?
> 
> Duane Rettig asked me to summarize what came out of this discussion in
> order to provide a reference for people reading this later on, so
> there you are:

Thanks for doing this, since this is a more general discussion than
if it were only a support issue.

> Pascal and Arthur showed a LispWorks-specific extension and other
> Lisps probably have similar offerings but there's no way you can
> achieve what I wanted with the MOP alone in a portable way (provided
> you consider the MOP itself to be portable).

Right; the MOP is functional in nature and does not provide hooks
into macroexpansions.  One might argue that it is a hole in the
MOP, but I believe that it is not, because the MOP for
macroexpansions _is_ in fact handled by macros and functions.

> As I suggested above and Marc has also pointed out you can write your
> own MY-DEFCLASS (you can of course even shadow CL:DEFCLASS and give
> the same name to your macro) which could look like this (untested):
> 
>   (defmacro my-defclass (class-name
>                          superclasses
>                          (&rest slot-specifiers)
>                          &rest class-options)
>     (let ((metaclass (second (find :metaclass class-options
>                                    :key #'first
>                                    :test #'eq))))
>       (cond ((eq metaclass 'foo-class)
>              (let* ((new-slot-specifiers
>                      (loop for slot-specifier in slot-specifiers
>                            when (and (listp slot-specifier)
>                                      (getf (rest slot-specifier) :foo-defaults))
>                              collect (append slot-specifier
>                                              (list :foo-defaults-closure
>                                                    `#'(lambda ()
>                                                         ,(getf (rest slot-specifier)
>                                                                :foo-defaults))))
>                            else collect slot-specifier)))
>                `(cl:defclass ,class-name ,superclasses
>                      (,@new-slot-specifiers)
>                   ,@class-options)))
>             (t `(cl:defclass ,class-name ,superclasses
>                      (,@slot-specifiers)
>                   ,@class-options)))))
> 
> This is portable and will leave the form in :FOO-DEFAULTS as is and
> add another slot :FOO-DEFAULTS-CLOSURE with a closure that properly
> captures the lexical environment.

I haven't tested this either, but it looks alright at first glance.

> The downside is that this macro doesn't play well with other macros
> that shadow CL:DEFCLASS themselves like, say, CLSQL's DEF-VIEW-CLASS.
> Looks like you can't have the cake and eat it, too.

Actually, I think you can, if the syntax of the other wrappers is the
same as defclass, like this one currently is - if you rename my-defclass
to something like wrap-class-definer, and make it look like this (also
untested):

 (defmacro wrap-class-definer (definer class-name superclasses ...)
   ...

   `(,definer ,class-name ...)
   ...)

then you can invoke it thus:

 (wrap-class-definer defclass foo ...)

or (if your example of def-view-class is syntactically similar):

 (wrap-class-definer def-view-class foo ...)

This is not much worse to type than the original form, and it
carries with it the actual name of the original definer in the
macro form (although not in the traditional function position).
Other aspects of the specific nature of this macro could be similarly
parameterized; it is part of the extremely helpful and powerful
nature of macros.

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Edi Weitz
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <uk6pmn852.fsf@agharta.de>
On 05 Feb 2005 09:46:03 -0800, Duane Rettig <·····@franz.com> wrote:

> Edi Weitz <········@agharta.de> writes:
>
>> The downside is that this macro doesn't play well with other macros
>> that shadow CL:DEFCLASS themselves like, say, CLSQL's
>> DEF-VIEW-CLASS.  Looks like you can't have the cake and eat it,
>> too.
>
> Actually, I think you can, if the syntax of the other wrappers is
> the same as defclass, like this one currently is - if you rename
> my-defclass to something like wrap-class-definer, and make it look
> like this (also untested):
>
>  (defmacro wrap-class-definer (definer class-name superclasses ...)
>    ...
>
>    `(,definer ,class-name ...)
>    ...)
>
> then you can invoke it thus:
>
>  (wrap-class-definer defclass foo ...)
>
> or (if your example of def-view-class is syntactically similar):
>
>  (wrap-class-definer def-view-class foo ...)
>
> This is not much worse to type than the original form, and it
> carries with it the actual name of the original definer in the macro
> form (although not in the traditional function position).  Other
> aspects of the specific nature of this macro could be similarly
> parameterized; it is part of the extremely helpful and powerful
> nature of macros.

Yes, good idea, thanks for the suggestion.

If you don't like the look of this WRAP- macro an alternative would be
something like

  (defmacro with-captured-closure
      ((definer class-name superclasses ...))
    ...)

which would be invoked like

  (with-captured-closure
    (cl:defclass ...))

or

  (with-captured-closure
    (clsql:def-view-class ...)).

Macros are great, aren't they?

Edi.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Duane Rettig
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <4vf966byt.fsf@franz.com>
Edi Weitz <········@agharta.de> writes:

> If you don't like the look of this WRAP- macro an alternative would be
> something like
> 
>   (defmacro with-captured-closure
>       ((definer class-name superclasses ...))
>     ...)
> 
> which would be invoked like
> 
>   (with-captured-closure
>     (cl:defclass ...))
> 
> or
> 
>   (with-captured-closure
>     (clsql:def-view-class ...)).

Yes, that's nice; it allows the form itself to be left entirely
alone, and just wrapped.

On final nit, though; unless your macro is willing to regognize all
sorts of syntax forms, the name is probably too general; perhaps my
"class-definer" name is too specific, but perhaps not, and the name
should be something like with-captured-defclass-like-closure instead.

Otherwise the macro would at least have to be willing to accept something
like

 (with-captured-closure
   (defun ...))

and not screw it up too badly.  Or, alternatively, it could recognize
the defclass syntax (looking for a particular shape) and just expand to
the inner form unchanged if it is not a defclass-like form.

> Macros are great, aren't they?

Yup.

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Pascal Costanza
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <cu7crm$mav$1@snic.vub.ac.be>
Edi Weitz wrote:

> Duane Rettig asked me to summarize what came out of this discussion in
> order to provide a reference for people reading this later on, so
> there you are:
> 
> Pascal and Arthur showed a LispWorks-specific extension and other
> Lisps probably have similar offerings but there's no way you can
> achieve what I wanted with the MOP alone in a portable way (provided
> you consider the MOP itself to be portable).
> 
> As I suggested above and Marc has also pointed out you can write your
> own MY-DEFCLASS (you can of course even shadow CL:DEFCLASS and give
> the same name to your macro) which could look like this (untested):
[...]

> The downside is that this macro doesn't play well with other macros
> that shadow CL:DEFCLASS themselves like, say, CLSQL's DEF-VIEW-CLASS.
> Looks like you can't have the cake and eat it, too.

Note that you would get similar problems at the MOP level. LispWorks' 
PROCESS-A-SLOT-OPTION must be specialized on a metaclass, and 
metaclasses are in general not compatible. That's why you have to 
explicitly declare them compatible via VALIDATE-SUPERCLASS.

It can be shown that it is not possible to automagically make 
metaclasses compatible to each other, so in general you can only solve 
this by making at least one of two metaclasses that you want to combine 
aware of the other. That's similar to writing a new defclass macro on 
top of another already redefined defclass.


Pascal
From: Edi Weitz
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <uu0ooem5l.fsf@agharta.de>
On Mon, 07 Feb 2005 10:36:54 +0100, Pascal Costanza <··@p-cos.net> wrote:

> Note that you would get similar problems at the MOP
> level. LispWorks' PROCESS-A-SLOT-OPTION must be specialized on a
> metaclass, and metaclasses are in general not compatible. That's why
> you have to explicitly declare them compatible via
> VALIDATE-SUPERCLASS.
>
> It can be shown that it is not possible to automagically make
> metaclasses compatible to each other, so in general you can only
> solve this by making at least one of two metaclasses that you want
> to combine aware of the other. That's similar to writing a new
> defclass macro on top of another already redefined defclass.

Yes, I kind of remembered that you talked about this in Hamburg last
year when you presented AspectL but I forgot the details.  Thanks for
the explanation.

Cheers,
Edi.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Jochen Jost
Subject: validate-superclass [Was: MOP question: custom slot options that behave like :INITFORM]
Date: 
Message-ID: <87fz04k2mx.fsf_-_@moutarde.epices.org>
Pascal Costanza <··@p-cos.net> writes:

> Note that you would get similar problems at the MOP level. LispWorks'
> PROCESS-A-SLOT-OPTION must be specialized on a metaclass, and
> metaclasses are in general not compatible. That's why you have to
> explicitly declare them compatible via VALIDATE-SUPERCLASS.

  Why is this necessary?  While playing with the examples from AMOP,
  ch. 3.1, I got an incompatible class error, which could be avoided
  with validate-superclass.

> It can be shown that it is not possible to automagically make
> metaclasses compatible to each other,

  It can be shown in general?  Then, what is the difference between

  (defclass counted-class (standard-class)
  	   ((counter :initform 0)))

  and

  (defmethod pcl:validate-superclass ((c1 counted-class) (c2 standard-class))
    t)

  regarding class-precedence-list?  Put another way, what I'm missing here?
  
> so in general you can only solve
> this by making at least one of two metaclasses that you want to
> combine aware of the other. That's similar to writing a new defclass
> macro on top of another already redefined defclass.

  Thanks,
  Jochen
From: Pascal Costanza
Subject: Re: validate-superclass [Was: MOP question: custom slot options that  behave like :INITFORM]
Date: 
Message-ID: <cug73i$dd7$1@snic.vub.ac.be>
Jochen Jost wrote:

> Pascal Costanza <··@p-cos.net> writes:
> 
>>Note that you would get similar problems at the MOP level. LispWorks'
>>PROCESS-A-SLOT-OPTION must be specialized on a metaclass, and
>>metaclasses are in general not compatible. That's why you have to
>>explicitly declare them compatible via VALIDATE-SUPERCLASS.
> 
>   Why is this necessary?  While playing with the examples from AMOP,
>   ch. 3.1, I got an incompatible class error, which could be avoided
>   with validate-superclass.
> 
>>It can be shown that it is not possible to automagically make
>>metaclasses compatible to each other,
> 
>   It can be shown in general?  Then, what is the difference between
> 
>   (defclass counted-class (standard-class)
>   	   ((counter :initform 0)))
> 
>   and
> 
>   (defmethod pcl:validate-superclass ((c1 counted-class) (c2 standard-class))
>     t)
> 
>   regarding class-precedence-list?  Put another way, what I'm missing here?

This doesn't have anything to do class-precedence-list, only with 
metaclass compatibility. You have just given one example in which two 
metaclass are compatible without further ado. This doesn't mean that 
this is always the case.

In general, two unrelated metaclasses are not compatible, and that's 
because of semantical issues. Imagine a metaclass that stores slot 
values in a hash table, and another one that stores slot values in an 
external database. There's no way to automagically combine the two, so 
either one metaclass needs to know about the other, or a third metaclass 
must combine the two.

Note that your example only works "automagically" because you, the 
implementor of counted-class, already know how standard-class works. (I 
don't know what your metaclass does, it probably counts instances or 
accesses to slots, or some such. Imagine another metaclass that changes 
the instance creation or slot access protocols so that your counting 
protocol doesn't work anymore. Those two metaclasses wouldn't be 
compatible anymore.)

Usually, one implements metaclasses that are compatible with 
standard-class, so specializing validate-superclass over and over again 
seems superfluous. But again, in general there is no guarantee that 
metaclasses are compatible with standard-class (for example 
structure-class or built-in-class), so the CLOS MOP has to perform some 
check.

The book "Putting Metaclasses to Work" by Ira Forman and Scott Danforth 
gives an overview about the potential conflicts between metaclasses



Pascal
From: Jochen Jost
Subject: Re: validate-superclass [Was: MOP question: custom slot options that behave like :INITFORM]
Date: 
Message-ID: <871xbnoorp.fsf@moutarde.epices.org>
Pascal Costanza <··@p-cos.net> writes:

> Jochen Jost wrote:
>
>> Pascal Costanza <··@p-cos.net> writes:
>>
>>
>>>It can be shown that it is not possible to automagically make
>>>metaclasses compatible to each other,
>>   It can be shown in general?  Then, what is the difference between
>>   (defclass counted-class (standard-class)
>>   	   ((counter :initform 0)))
>>   and
>>   (defmethod pcl:validate-superclass ((c1 counted-class) (c2
>> standard-class))
>>     t)
>>   regarding class-precedence-list?  Put another way, what I'm
>> missing here?
>
> This doesn't have anything to do class-precedence-list, only with
> metaclass compatibility. You have just given one example in which two
> metaclass are compatible without further ado. This doesn't mean that
> this is always the case.
>
> In general, two unrelated metaclasses are not compatible, and that's
> because of semantical issues.

  Yes, I guessed as much, but wondered about the need for
  validate-superclass in this particular case, when the stated purpose
  of validate-superclass is "to determine whether the class
  /superclass/ is suitable for use as a superclass of /class/."  (from
  AMOP).  And yes, I confused metaclass compatibility with
  class-precedence-list as I thought the (defclass counted-class ...)
  above already did this validation thing and so the metaclasses
  weren't unrelated anymore.

> Note that your example only works "automagically" because you, the

  No, it didn't, hence my questions :)

> implementor of counted-class, already know how standard-class
> works. (I don't know what your metaclass does, it probably counts
> instances or accesses to slots, or some such. Imagine another
> metaclass that changes the instance creation or slot access protocols
> so that your counting protocol doesn't work anymore. Those two
> metaclasses wouldn't be compatible anymore.)
>
> Usually, one implements metaclasses that are compatible with
> standard-class, so specializing validate-superclass over and over
> again seems superfluous.

  Exactly.  Why can't the machinery figure it out in this rather
  simple case?  -- But, not that this is really important; I'm asking
  out of curiosity.

> The book "Putting Metaclasses to Work" by Ira Forman and Scott
> Danforth gives an overview about the potential conflicts between
> metaclasses

  I googled "metaclass compatibility" (by the way: is this a well
  defined term with a definition like "Two metaclasses are compatible
  when ..."?) and found some interesting papers, and will have a look
  at Forman and Danforth when I'll have some time to spare.  Thanks
  for this reference and the detailed explanation,

  Jochen
From: Rahul Jain
Subject: Re: validate-superclass
Date: 
Message-ID: <87psz7errc.fsf@nyct.net>
Jochen Jost <···········@tele2.fr> writes:

>> Usually, one implements metaclasses that are compatible with
>> standard-class, so specializing validate-superclass over and over
>> again seems superfluous.
>
>   Exactly.  Why can't the machinery figure it out in this rather
>   simple case?  -- But, not that this is really important; I'm asking
>   out of curiosity.

For the cases when it's not true, I suppose. The default could be to
assume compatibility with standard-classes as superclasses and require
explicit overriding if that's not the case. Maybe that would be more
useful, but then you're assuming the non-existence of other, parallel
hierarchies of metaclasses, incompatible with standard-class.

>> The book "Putting Metaclasses to Work" by Ira Forman and Scott
>> Danforth gives an overview about the potential conflicts between
>> metaclasses
>
>   I googled "metaclass compatibility" (by the way: is this a well
>   defined term with a definition like "Two metaclasses are compatible
>   when ..."?)

Metaclass A is compatible with metaclass B when a class can be defined
with metaclass A and have a superclass with metaclass B. Note that the
relation is not necessarily commutative.

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
From: Pascal Costanza
Subject: Re: validate-superclass [Was: MOP question: custom slot options  that behave like :INITFORM]
Date: 
Message-ID: <cuivpo$rus$1@snic.vub.ac.be>
Jochen Jost wrote:

>>Note that your example only works "automagically" because you, the
> 
>   No, it didn't, hence my questions :)

OK, make that "your example _would_ only work ...". ;)

>>Usually, one implements metaclasses that are compatible with
>>standard-class, so specializing validate-superclass over and over
>>again seems superfluous.
> 
>   Exactly.  Why can't the machinery figure it out in this rather
>   simple case?  -- But, not that this is really important; I'm asking
>   out of curiosity.

It seems to me that there some work has been done on this issue, but on 
the other hand I don't think it's that important. I think living with 
validate-superclass is good enough.

>>The book "Putting Metaclasses to Work" by Ira Forman and Scott
>>Danforth gives an overview about the potential conflicts between
>>metaclasses
> 
>   I googled "metaclass compatibility" (by the way: is this a well
>   defined term with a definition like "Two metaclasses are compatible
>   when ..."?) and found some interesting papers, and will have a look
>   at Forman and Danforth when I'll have some time to spare.  Thanks
>   for this reference and the detailed explanation,

I actually haven't googled for this term. Interesting papers indeed. I 
should read them. ;)

However, I get suspicious when they only seem to tackle syntactical 
stuff, like who is accessing which variables and who is overriding which 
methods. I don't think these are the interesting cases of metaclass 
incompatibility. The interesting cases are at the semantical level, and 
they are most likely not solveable.


Pascal
From: Edi Weitz
Subject: Re: MOP question: custom slot options that behave like :INITFORM
Date: 
Message-ID: <uacqfbczu.fsf@agharta.de>
On Sat, 05 Feb 2005 17:21:20 +0100, Edi Weitz <········@agharta.de> wrote:

> As I suggested above and Marc has also pointed out you can write
> your own MY-DEFCLASS (you can of course even shadow CL:DEFCLASS and
> give the same name to your macro) which could look like this
> (untested):
>
>   (defmacro my-defclass (class-name
>                          superclasses
>                          (&rest slot-specifiers)
>                          &rest class-options)
>     (let ((metaclass (second (find :metaclass class-options
>                                    :key #'first
>                                    :test #'eq))))
>       (cond ((eq metaclass 'foo-class)
>              (let* ((new-slot-specifiers
>                      (loop for slot-specifier in slot-specifiers
>                            when (and (listp slot-specifier)
>                                      (getf (rest slot-specifier) :foo-defaults))
>                              collect (append slot-specifier
>                                              (list :foo-defaults-closure
>                                                    `#'(lambda ()
>                                                         ,(getf (rest slot-specifier)
>                                                                :foo-defaults))))
>                            else collect slot-specifier)))
>                `(cl:defclass ,class-name ,superclasses
>                      (,@new-slot-specifiers)
>                   ,@class-options)))
>             (t `(cl:defclass ,class-name ,superclasses
>                      (,@slot-specifiers)
>                   ,@class-options)))))
>
> This is portable and will leave the form in :FOO-DEFAULTS as is and
> add another slot :FOO-DEFAULTS-CLOSURE with a closure that properly
> captures the lexical environment.

So I thought whatever gets posted to comp.lang.lisp will be
scrutinized and corrected until the last erroneous comma has been
eradicated.  But that doesn't seem to be the case anymore.  The
untested code above is wrong, folks... :)

With this macro the :FOO-DEFAULTS-CLOSURE slot will hold a cons, not a
closure.  You can modify the macro such that it expands into a
function object instead of the lambda form at this place but then
you'll have an object which is generally not externalizable and there
goes portability.

FWIW, below is a simplified version of what I'm doing now.  (In real
life you probably have more than one such slot and want the hash table
to keep track of the slot names as well.)  It feels kind of kludgy but
it seems to work.  If someone has a more elegant or more general
solution I'd be happy to see it.

You can get rid of the hash table kludge if you expand directly into
MOP functions - you can look, say, at the CMUCL sources to see how
they fill the SLOT-DEFINITION-INITFUNCTION slot.  Of course, that's
another solution that's not portable.

Cheers,
Edi.



(in-package :cl-user)

(use-package #+(or :allegro :cmu) :mop
             #+:lispworks :clos)

(defvar *secret-hash* (make-hash-table :test #'eq))

(defclass foo-class (standard-class) ())

(defmethod validate-superclass ((class foo-class) (superclass standard-class))
  t)

(defclass foo-class-slot-definition-mixin ()
     ((foo-defaults :accessor foo-defaults
                    :initarg :foo-defaults
                    :initform nil)
      (foo-defaults-closure :accessor foo-defaults-closure
                            :initform nil)))

(defclass foo-class-direct-slot-definition (foo-class-slot-definition-mixin
                                            standard-direct-slot-definition)
     ())

(defmethod direct-slot-definition-class ((class foo-class) &rest initargs)
  (declare (ignore initargs))
  (find-class 'foo-class-direct-slot-definition))

(defclass foo-class-effective-slot-definition (foo-class-slot-definition-mixin
                                               standard-effective-slot-definition)
     ())

(defmethod compute-effective-slot-definition
    ((class foo-class) slot-name direct-slot-definitions)
  (declare (ignore slot-name))
  (let ((effective-slot-definition (call-next-method)))
    (setf (foo-defaults effective-slot-definition)
            (foo-defaults (first direct-slot-definitions))
          (foo-defaults-closure effective-slot-definition)
            (gethash (class-name class) *secret-hash*))
    effective-slot-definition))

(defmethod effective-slot-definition-class ((class foo-class) &rest initargs)
  (declare (ignore initargs))
  (find-class 'foo-class-effective-slot-definition))

(defmacro defclass* (class-name
                     superclasses
                     (&rest slot-specifiers)
                     &rest class-options)
  (let ((metaclass (second (find :metaclass class-options
                                 :key #'first
                                 :test #'eq))))
    (cond ((eq metaclass 'foo-class)
           (let (closure)
             (dolist (slot-specifier slot-specifiers)
               (when (and (listp slot-specifier)
                          (getf (rest slot-specifier) :foo-defaults))
                 (setq closure
                         `(lambda ()
                            ,(getf (rest slot-specifier)
                                   :foo-defaults)))))
             `(progn
                ,@(when closure
                    `((eval-when (:compile-toplevel :load-toplevel :execute)
                        (setf (gethash ',class-name *secret-hash*)
                                ,closure))))
                (cl:defclass ,class-name ,superclasses
                     (,@slot-specifiers)
                  ,@class-options))))
          (t `(cl:defclass ,class-name ,superclasses
                   (,@slot-specifiers)
                ,@class-options)))))

(let ((bar 42))
  (defclass* foo ()
    ((x :initform bar
        :foo-defaults bar))
    (:metaclass foo-class)))

(defun check ()
  (let* ((object (make-instance 'foo))
         (slot (first (class-slots (class-of object)))))
    (list (funcall (slot-definition-initfunction slot))
          (funcall (foo-defaults-closure slot)))))


-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")