From: Sacha
Subject: intercepting (setf accessor)
Date: 
Message-ID: <795Tf.324270$bT4.10332321@phobos.telenet-ops.be>
Still got more newbie questions.
This time it's more about architecture.

Let's say i have a class defined as follow :

(defclass item ()
     ((parent :accessor parent)))

What i want is a way to intercept calls to (setf (parent item-instance) 
parent-object)
in order to validate the new value, or maybe update an index or a database,
or whatever along those lines...

How would i go about doing this ?
I'd rather avoid MOP if possible.
That's why i don't really try intercepting (setf (slot-value item-instance 
'parent) parent-object)

I thought about making eql specialisers on the fly, maybe using a
"closure builder" then releasing it when done with the item ...

but
1: i'm afraid this might be like using a cannon to kill a fly
2: i'm not quite sure how to do this

Also i don't really want to use a library that would do this
for me, as the real goal is learning.

Any ideas would be greatly apreciated.

Sacha 

From: Dvd Avins
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <1142747628.487837.165970@i39g2000cwa.googlegroups.com>
I'm a noob, too, but I think setting a bi-directional accessor is most
appropriate when you *dont* want to intercept such puts. Why not define
just a getter and also define a gf that sets the value with the
validation wyou want?
From: Ken Tilton
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <0y5Tf.3758$jw.1356@fe09.lga>
Sacha wrote:
> Still got more newbie questions.
> This time it's more about architecture.
> 
> Let's say i have a class defined as follow :
> 
> (defclass item ()
>      ((parent :accessor parent)))
> 
> What i want is a way to intercept calls to (setf (parent item-instance) 
> parent-object)

(defmethod (setf parent) :around (new-value (self item))
     (if <everything is cool>
          (call-next-method)
         (break "what on earth are you thinking about?")))

other ways to augment are :before and :after where you see :around, but 
:around let's you play with new-value or see the result of the normal 
method (not to shocking in this case since we all know what setf does, 
but I am talking about GFs in general.

Find something somewhere on :before, :after, :around, and 
(call-next-method) and learn those. Pretty simple, very necessary.

kt


-- 
Cells: http://common-lisp.net/project/cells/

"And I will know my song well before I start singing."  - Bob Dylan
From: Sacha
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <rN5Tf.324322$4A1.10358835@phobos.telenet-ops.be>
>> Let's say i have a class defined as follow :
>>
>> (defclass item ()
>>      ((parent :accessor parent)))
>>
>> What i want is a way to intercept calls to (setf (parent item-instance) 
>> parent-object)
>
> (defmethod (setf parent) :around (new-value (self item))
>     (if <everything is cool>
>          (call-next-method)
>         (break "what on earth are you thinking about?")))
>
> other ways to augment are :before and :after where you see :around, but 
> :around let's you play with new-value or see the result of the normal 
> method (not to shocking in this case since we all know what setf does, but 
> I am talking about GFs in general.
>
> Find something somewhere on :before, :after, :around, and 
> (call-next-method) and learn those. Pretty simple, very necessary.


I thought this was only for "more specialized" methods, good to know i can 
do it like this.
But really that's not what i want to do.
Let's take a precise example.

I have this item object which could in an index or not.

I want to be able to define my index without the item class knowning about 
it.
No reference to the index in the "item" class.

When the item is added to my index, the index should be warned about changes 
to the indexed fields.
That's at this point that i want the index to "register" for item "slot 
change events".
When the item is removed, the index should not be warned anymore. So there 
should be some kind of "unregistering".
The index object should be generic in the sense that i don't want to have a 
subclass of it
for every classes that will one day or another be indexed
Also i'm not sure an EQL specialiser could be created on every insert.
And not sure either that this would be the good thing to do.
And not sure how to do this anyways =P

Sacha
From: Dvd Avins
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <1142747832.994726.318500@i40g2000cwc.googlegroups.com>
Does each class have its own index, or do all potential classes share
the same index?
From: Sacha
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <4P6Tf.324409$Mf4.10177580@phobos.telenet-ops.be>
"Dvd Avins" <········@pobox.com> wrote in message 
·····························@i40g2000cwc.googlegroups.com...
> Does each class have its own index, or do all potential classes share
> the same index?

In my example, the goal would be to mimic a relational database.
So there would be only one class per index. But there are several indexes 
per class.

>I'm a noob, too, but I think setting a bi-directional accessor is most
>appropriate when you *dont* want to intercept such puts. Why not define
>just a getter and also define a gf that sets the value with the
>validation wyou want?

Well i don't care too much for the bi-directional accessor, so it may go =P
For the setter, i would like to register for interception of these calls at 
run time instead of
defining a setter in the source.

To go back to the relational database example, who knows when a user will 
define
another index on a table ?

I think i will have to define setters manually and provide an event for each 
of these like this :

(defclass item ()
   ((name :reader name)
    (name-changing :accessor name-changing)))
;;Very delphi/c#/c++ like, isn't it ?

(defmethod (setf name) (value (item item))
   (when name-changing (funcall (name-changing value item)))
   (setf (slot-value item 'name) value))

This way the index could register with the "name-changing" event for a
specific instance of the class item.... but i hoped there would be
some other way to do this.

So in the end, i'm back at the point where i'll need to define a macro 
looking like this :

(defrow item ()
   ((name :with-event t)
     (non-indexable-slot :accessor blah ....)))

And save metadata in a dynamic variable so that the index
knows how to register for the event.

I was hoping to use a simple defclass and start ntercepting the changes to 
the name
slot/accessor once the item is inserted in my index.

Sacha 
From: Dvd Avins
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <1142755169.628660.80540@g10g2000cwb.googlegroups.com>
I don't understand why you need a slot for name-changing.

This is not tested and I'd be surprised if I got it all right, but
would the general idea suit your purpose?

(defclass person (table-row)
    ((first-name :reader first-name)
     (middle-initial :accessor initial)
     (last-name :reader last-name)
        (indicies :allocation :class :initform (progn
                (setf 'item first-name-index (new-index))
                (setf 'item last-name-index (new-index))))))

(defmethod set-indexed-value ((class-name symbol) (row table-row)
(column-name symbol) value)
    (unwind-protect (update-index (get class-name column-name)
(slot-value table-row column-name) value)
        (setf (slot-value table-row column-name) value)))

You'd still need to define your indexe3s and the methods for updating
them. And I left out the complication of the old value sometimes being
NIL and what to do about that.

You could have a macroo that would take a table name and lists of
indexed and unindexed slots that would generate the code above. Another
macro could convert an undixed slot to an indexed one.
From: Sacha
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <4K8Tf.324620$2B3.10207278@phobos.telenet-ops.be>
> This is not tested and I'd be surprised if I got it all right, but
> would the general idea suit your purpose?
>
> (defclass person (table-row)
>    ((first-name :reader first-name)
>     (middle-initial :accessor initial)
>     (last-name :reader last-name)
>        (indicies :allocation :class :initform (progn
>                (setf 'item first-name-index (new-index))
>                (setf 'item last-name-index (new-index))))))
>
> (defmethod set-indexed-value ((class-name symbol) (row table-row)
> (column-name symbol) value)
>    (unwind-protect (update-index (get class-name column-name)
> (slot-value table-row column-name) value)
>        (setf (slot-value table-row column-name) value)))
>

I'm not quite sure to fully understand what you mean here...
But it looks like everything would be hard-coded then.
And also i'd need to update my slot values through this set-indexed-value 
method.

Indexing a person would'nt be transparent to the person class.

I think i'll finally investigate MOP.
The (setf slot-value-using-class) method route might be easier after all.
Though i really hoped for a simpler pattern to do this..

Sacha 
From: Kaz Kylheku
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <1142795045.031308.313860@z34g2000cwc.googlegroups.com>
Sacha wrote:
> This way the index could register with the "name-changing" event for a
> specific instance of the class item.... but i hoped there would be
> some other way to do this.

This sounds like a job for aspect-oriented programming. You have some
join points: the methods that modify certain slots in the object that
you are indexing on. A point cut: selection of all these methods across
a class. And advice: instrumenting these modifications so that they
take some additional action, namely update an index whose existence the
objects don't even know about.

> I was hoping to use a simple defclass and start ntercepting the changes to
> the name
> slot/accessor once the item is inserted in my index.

It's quite possible that AspectL has a nice solution to this problem,
so yo don't have to re-invent your own.
From: Sacha
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <nJmTf.326119$4A1.10358835@phobos.telenet-ops.be>
"Kaz Kylheku" <········@gmail.com> wrote in message 
·····························@z34g2000cwc.googlegroups.com...
> Sacha wrote:
>> This way the index could register with the "name-changing" event for a
>> specific instance of the class item.... but i hoped there would be
>> some other way to do this.
>
> This sounds like a job for aspect-oriented programming. You have some
> join points: the methods that modify certain slots in the object that
> you are indexing on. A point cut: selection of all these methods across
> a class. And advice: instrumenting these modifications so that they
> take some additional action, namely update an index whose existence the
> objects don't even know about.
>
>> I was hoping to use a simple defclass and start ntercepting the changes 
>> to
>> the name
>> slot/accessor once the item is inserted in my index.
>
> It's quite possible that AspectL has a nice solution to this problem,
> so yo don't have to re-invent your own.
>

The fact is that I pretty much ignored AOP until now.
I'll have a look to it during the week !

thanks a lot
Sacha
From: Pascal Costanza
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <4875e9Fir1fuU1@individual.net>
Kaz Kylheku wrote:
> Sacha wrote:
> 
>>This way the index could register with the "name-changing" event for a
>>specific instance of the class item.... but i hoped there would be
>>some other way to do this.
> 
> This sounds like a job for aspect-oriented programming. You have some
> join points: the methods that modify certain slots in the object that
> you are indexing on. A point cut: selection of all these methods across
> a class. And advice: instrumenting these modifications so that they
> take some additional action, namely update an index whose existence the
> objects don't even know about.> 
>>I was hoping to use a simple defclass and start ntercepting the changes to
>>the name
>>slot/accessor once the item is inserted in my index.
> 
> It's quite possible that AspectL has a nice solution to this problem,
> so yo don't have to re-invent your own.

I'd rather do it with the CLOS MOP.


Pascal

-- 
3rd European Lisp Workshop
July 3-4 - Nantes, France - co-located with ECOOP 2006
http://lisp-ecoop06.bknr.net/
From: Sacha
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <oEtTf.326698$8F4.10289300@phobos.telenet-ops.be>
>> It's quite possible that AspectL has a nice solution to this problem,
>> so yo don't have to re-invent your own.
>
> I'd rather do it with the CLOS MOP.
>
> Pascal
>

Since i got it working with mop, I'll just stick to it.
The goal is attained : i can define new classes that will be indexable
without these to know about indexing (only adding a :metaclass
and super class does the trick now)

I nevertheless downloaded your AspectL library, and will play with it.
Though I wonder what's the runtime cost of using such tools.

Sacha 
From: Pascal Costanza
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <487eqtFi5ac4U1@individual.net>
Sacha wrote:
>>>It's quite possible that AspectL has a nice solution to this problem,
>>>so yo don't have to re-invent your own.
>>
>>I'd rather do it with the CLOS MOP.
>>
>>Pascal
> 
> Since i got it working with mop, I'll just stick to it.
> The goal is attained : i can define new classes that will be indexable
> without these to know about indexing (only adding a :metaclass
> and super class does the trick now)
> 
> I nevertheless downloaded your AspectL library, and will play with it.
> Though I wonder what's the runtime cost of using such tools.

AspectL is indeed a collection of four different features:

- A generic pointcut is a data structure into which you can add methods 
on the one hand and "aspect weavers" on the other. The aspect weavers 
define how to add typically before, after or around methods to the 
methods that were or are about to be added to a generic pointcut. Since 
the resulting methods are all plain CLOS methods, there is no runtime 
overhead to be expected, apart from the "weaving" that takes place 
whenever you change the contents of a generic pointcut.

By now, I doubt the usefulness of this construct. There isn't a lot that 
you can achieve here that you couldn't as well achieve with macro 
programming. An important difference is that "macro expansion" in 
generic pointcuts happens at runtime, not at compile time. However, I 
haven't seen a good example of how to make use of that yet. (Note that I 
didn't say that this provides some notion of runtime macros, that would 
be something quite different.)


- The destructive mixins are a way to change class definitions at 
runtime. You can add/remove/change slots at runtime and modify the class 
hierarchy as well. This is the one feature that a few people found 
useful (which surprised me). Again, there is no runtime overhead because 
I base everything on plain CLOS here. Unfortunately, I don't do a lot of 
error checking, which makes it quite easy to screw things up. This 
happened to me a number of times that I made a change to class which 
didn't fit its current structure, and this screwed the system up so 
heavily that all I could do was shut down and restart the respective CL 
environment. Currently, I don't have any plans to improve this 
situation. The reason is that I think I have found a more declarative, 
easier to use way to do more or less the same thing with ContextL.


- The dletf framework / the special slots provide a way to rebind slots 
in classes / objects with dynamic scope, without affecting the binding 
seen in other threads. This is similar to the behavior of special 
variables. This I consider very useful - a few people are already using 
this in practice. There is runtime overhead involved, but only 
restricted to accesses to slots that are actually special. Other slots 
are unaffected here.


- Special generic functions provide a way to add methods to a generic 
function with dynamic scope. That is, in a given dynamic scope, you can 
add methods to change the behavior of a generic function, but after 
return from that dynamic scope, the methods are automatically removed 
again. Again, other threads are not affected by such changes, they 
simply don't see the newly added methods. The runtime overhead is very 
high, because whenever a method is added / remove, the caches of a 
generic function have to be reinitialized, and that does affect other 
threads as well. There is a conceptual problem here, so it's hard to 
resolve this. It's basically the CLOS MOP doesn't provide enough access 
to the internals of generic function dispatch to tweak this further.

ContextL provides a similar concept, with changed semantics, and in my 
opinion a much better interface. The change in semantics has two 
advantages: It both makes the concept easier to use and to modularize, 
and it makes it easier to implement it very efficiently. You can indeed 
change the definition of a generic function with dynamic scope, without 
any serious runtime overhead. You more or less get the same speed as 
with regular generic function dispatch in plain CLOS when you use ContextL.


In other words, I would recommend to skip AspectL and to take a look at 
ContextL instead. In my opinion, ContextL drops the features of AOP that 
don't make a lot of sense in Common Lisp (which actually that it's not 
AOP anymore), and provides the useful features in a nicer and more 
efficient variant. There are already some people who use ContextL in 
their own software, and I am aware of at least one project where it used 
in "real-world" software. So I also think that at its early age, 
ContextL is already relatively mature.


Pascal

-- 
3rd European Lisp Workshop
July 3-4 - Nantes, France - co-located with ECOOP 2006
http://lisp-ecoop06.bknr.net/
From: Sacha
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <IRBTf.327467$R6.10166385@phobos.telenet-ops.be>
> In other words, I would recommend to skip AspectL and to take a look at 
> ContextL instead. In my opinion, ContextL drops the features of AOP that 
> don't make a lot of sense in Common Lisp (which actually that it's not AOP 
> anymore), and provides the useful features in a nicer and more efficient 
> variant. There are already some people who use ContextL in their own 
> software, and I am aware of at least one project where it used in 
> "real-world" software. So I also think that at its early age, ContextL is 
> already relatively mature.
>
>
> Pascal

I always felt a bit uneasy with AOP, that's how i choose
to ignore it. Language support was pretty bad too for those
languages i've been working with.

AOP introduces a whole lot of new naming conventions,
and it's pretty hard to dive in without a good language support.
Using pre-processors or post-compilation tools seems a bit awkward
when programing in c# for instance.

Also, you know how it goes, the customer wants its stuff done for yesterday.
So many new concepts, so little time....

The one thing that has always bothered me with oop, is how my classes are
getting more and more dependents on other classes. The very reason for
oop disapears as the project grows. This separation of concern is the grail
i'm pursuing while learning lisp. It looks like AOP can help on this, and
ContextL too.

I remember reading your paper on ContextL a while back.
The concept seemed interesting enough. Once i'll feel a bit more at home
using CL , I'll follow your advice and go back to it.

Thanks for all these informations.

Sacha
From: Ken Tilton
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <8P7Tf.767$en2.421@fe08.lga>
Sacha wrote:
>>>Let's say i have a class defined as follow :
>>>
>>>(defclass item ()
>>>     ((parent :accessor parent)))
>>>
>>>What i want is a way to intercept calls to (setf (parent item-instance) 
>>>parent-object)
>>
>>(defmethod (setf parent) :around (new-value (self item))
>>    (if <everything is cool>
>>         (call-next-method)
>>        (break "what on earth are you thinking about?")))
>>
>>other ways to augment are :before and :after where you see :around, but 
>>:around let's you play with new-value or see the result of the normal 
>>method (not to shocking in this case since we all know what setf does, but 
>>I am talking about GFs in general.
>>
>>Find something somewhere on :before, :after, :around, and 
>>(call-next-method) and learn those. Pretty simple, very necessary.
> 
> 
> 
> I thought this was only for "more specialized" methods, good to know i can 
> do it like this.
> But really that's not what i want to do.
> Let's take a precise example.
> 
> I have this item object which could in an index or not.
> 
> I want to be able to define my index without the item class knowning about 
> it.
> No reference to the index in the "item" class.
> 
> When the item is added to my index, the index should be warned about changes 
> to the indexed fields.
> That's at this point that i want the index to "register" for item "slot 
> change events".
> When the item is removed, the index should not be warned anymore. So there 
> should be some kind of "unregistering".
> The index object should be generic in the sense that i don't want to have a 
> subclass of it
> for every classes that will one day or another be indexed
> Also i'm not sure an EQL specialiser could be created on every insert.
> And not sure either that this would be the good thing to do.
> And not sure how to do this anyways =P


You may be thinking in C++. There t he interceptor method would have to 
be built into the class. That is not CLOS. In CLOS, once a class is 
defined, any other subsystem can specialize methods on that class, 
without any cooperation from that class.

kt
From: Ivan Shvedunov
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <484nvtFhstbrU1@individual.net>
Maybe I'm wrong, but if you want to intercept slot changes in rather
general case it seems to me that while it's possible to avoid
MOP with some hacks, (SETF SLOT-VALUE-USING-CLASS)
http://www.lisp.org/mop/dictionary.html#(setf slot-value-using-class)
seems to be just what you need. Note though that e.g. in LW it is
implemented in not quite AMOP way (slot name is passed instead of
slot metaobject and default accessor implementations bypass this
method). This can be fixed by using Closer to MOP library:
http://common-lisp.net/project/closer/

Sacha wrote:
> Still got more newbie questions.
> This time it's more about architecture.
> 
> Let's say i have a class defined as follow :
> 
> (defclass item ()
>      ((parent :accessor parent)))
> 
> What i want is a way to intercept calls to (setf (parent item-instance) 
> parent-object)
> in order to validate the new value, or maybe update an index or a database,
> or whatever along those lines...
> 
> How would i go about doing this ?
> I'd rather avoid MOP if possible.
> That's why i don't really try intercepting (setf (slot-value item-instance 
> 'parent) parent-object)
> 
> I thought about making eql specialisers on the fly, maybe using a
> "closure builder" then releasing it when done with the item ...
> 
> but
> 1: i'm afraid this might be like using a cannon to kill a fly
> 2: i'm not quite sure how to do this
> 
> Also i don't really want to use a library that would do this
> for me, as the real goal is learning.
> 
> Any ideas would be greatly apreciated.
> 
> Sacha 
> 
> 
From: Sacha
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <GebTf.324903$Ak7.10347545@phobos.telenet-ops.be>
"Ivan Shvedunov" <·······@depni.sinp.msu.ru> wrote in message 
···················@individual.net...
> Maybe I'm wrong, but if you want to intercept slot changes in rather
> general case it seems to me that while it's possible to avoid
> MOP with some hacks, (SETF SLOT-VALUE-USING-CLASS)
> http://www.lisp.org/mop/dictionary.html#(setf slot-value-using-class)
> seems to be just what you need. Note though that e.g. in LW it is
> implemented in not quite AMOP way (slot name is passed instead of
> slot metaobject and default accessor implementations bypass this
> method). This can be fixed by using Closer to MOP library:
> http://common-lisp.net/project/closer/
>

Yep i came to the conclusion that's the way to go.
Too bad about the default accessor implementation in lispworks as that's
the one i use =/

Sacha 
From: Sacha
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <QbeTf.325230$Mf4.10177580@phobos.telenet-ops.be>
"Sacha" <··@address.spam> wrote in message 
······························@phobos.telenet-ops.be...
>
> "Ivan Shvedunov" <·······@depni.sinp.msu.ru> wrote in message 
> ···················@individual.net...
>> Maybe I'm wrong, but if you want to intercept slot changes in rather
>> general case it seems to me that while it's possible to avoid
>> MOP with some hacks, (SETF SLOT-VALUE-USING-CLASS)
>> http://www.lisp.org/mop/dictionary.html#(setf slot-value-using-class)
>> seems to be just what you need. Note though that e.g. in LW it is
>> implemented in not quite AMOP way (slot name is passed instead of
>> slot metaobject and default accessor implementations bypass this
>> method). This can be fixed by using Closer to MOP library:
>> http://common-lisp.net/project/closer/
>>
>
> Yep i came to the conclusion that's the way to go.
> Too bad about the default accessor implementation in lispworks as that's
> the one i use =/

Ok so here is a test case ...not working =P

(defclass my-meta-class (standard-class)
   ())

(defclass test-class ()
  ((aslot :initform nil))
  (:metaclass my-meta-class)
  (:optimize-slot-access nil))

(defmethod (setf slot-value-using-class) :around (value (class 
my-meta-class) object slot-name)
  (print "hellooo")
  (call-next-method))

(setq test-instance (make-instance 'test-class))

CL-USER 81 > (setf (slot-value test-instance 'aslot) 'test-value)
TEST-VALUE

CL-USER 82 >

I expected to see "hellooo" printed here,
It seems the (setf slot-value-using-class) is never called.

I'm using lispworks 4.4.6
According to their manual, the (:optimize-slot-access nil) form takes care 
of the accessors problem.
There must be some obvious mistake i'm doing here...
I didn't try the same thing using the closer lirary yet, since it should not 
be necessary according
to the lispworks manual, and i'd rather use it only if absolutely necessary. 
(portability is of no concern to me)

Sacha 
From: Sacha
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <GpeTf.325249$fT4.10336421@phobos.telenet-ops.be>
> Ok so here is a test case ...not working =P

hum ...forgot to (use-package :clos)
all working fine now

Sacha 
From: Ivan Shvedunov
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <485hpcFib9tkU1@individual.net>
Not sure whether this is a good advice, but if you don't want to mess
with metaclasses, you may specialize (setf slot-value-using-class)
on OBJECT argument using some mix-in class.
From: Pascal Costanza
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <485jhpFi9mcvU1@individual.net>
Ivan Shvedunov wrote:
> Not sure whether this is a good advice, but if you don't want to mess
> with metaclasses, you may specialize (setf slot-value-using-class)
> on OBJECT argument using some mix-in class.

That's not a good idea. There is wording in the CLOS MOP specification 
that suggests that the object argument in the slot-xxx-using-class 
functions should never be specialized. Unfortunately, the wording is 
somewhat ambiguous, but there are a few CLOS MOP implementations that 
indeed do not take specializations of the object argument into account. 
So in general, you should only specialize the class and/or the 
slot-definition argument.


Pascal

-- 
3rd European Lisp Workshop
July 3-4 - Nantes, France - co-located with ECOOP 2006
http://lisp-ecoop06.bknr.net/
From: Sacha
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <DyhTf.325586$8T4.10329223@phobos.telenet-ops.be>
Thanks all, I now have everything working like a charm.
It was bad to shy away from MOP, this part was really quite easy to figure 
out after all.
Stay tuned for more newbish quizzes =P

Sacha 
From: Arthur Lemmens
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <op.s6obswt8wpmq96@news.xs4all.nl>
Pascal Costanza <··@p-cos.net> wrote:

> That's not a good idea. There is wording in the CLOS MOP specification
> that suggests that the object argument in the slot-xxx-using-class
> functions should never be specialized.

Ah, interesting.  Could you tell me where I can find this 'wording' in
the AMOP (I assume that's what you meant with 'CLOS MOP specification')?

> there are a few CLOS MOP implementations that indeed do not take
> specializations of the object argument into account.

Here too I'd be interested in the specifics (maybe by email, if you
prefer not to be harsh in public).

Thanks,

Arthur
From: Pascal Costanza
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <487241Fifjt6U1@individual.net>
Arthur Lemmens wrote:
> Pascal Costanza <··@p-cos.net> wrote:
> 
>> That's not a good idea. There is wording in the CLOS MOP specification
>> that suggests that the object argument in the slot-xxx-using-class
>> functions should never be specialized.
> 
> Ah, interesting.  Could you tell me where I can find this 'wording' in
> the AMOP (I assume that's what you meant with 'CLOS MOP specification')?

It's in the section "Restrictions on Portable Programs": "Any method 
defined by a portable program on a specified generic function must have 
at least one specializer that is neither a specified class nor an eql 
specializer whose associated value is an instance of a specified class."

The ambiguity lies in the term "specified class". Search for "specified" 
in the CLOS MOP specification, and you will find a definition that could 
be interpreted as meaning only the metaobject classes. There is also a 
discussion in the CLOS MOP mailing list archive where the designers talk 
about this issue, and from that discussion, it's clearer that they meant 
that the object argument in slot-xxx-using-class should not be specialized.

>> there are a few CLOS MOP implementations that indeed do not take
>> specializations of the object argument into account.
> 
> Here too I'd be interested in the specifics (maybe by email, if you
> prefer not to be harsh in public).

IIRC, at least MCL, maybe OpenMCL, SBCL and probably CMUCL disregard 
specializations of that argument. Maybe the other implementations 
disregard them as well, but I haven't checked this for all implementations.

Christophe Rhodes told me that for SBCL (and so probably for CMUCL and 
other implementations based on PCL), this restriction could probably be 
dropped. Note that the slot-xxx-using-class functions were changed very 
late in the game before the AMOP book got published.


Pascal

-- 
3rd European Lisp Workshop
July 3-4 - Nantes, France - co-located with ECOOP 2006
http://lisp-ecoop06.bknr.net/
From: Christophe Rhodes
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <sq3bhdtmum.fsf@cam.ac.uk>
Pascal Costanza <··@p-cos.net> writes:

> Arthur Lemmens wrote:
>> Pascal Costanza <··@p-cos.net> wrote:
>>
>>> there are a few CLOS MOP implementations that indeed do not take
>>> specializations of the object argument into account.
>> Here too I'd be interested in the specifics (maybe by email, if you
>> prefer not to be harsh in public).
>
> Christophe Rhodes told me that for SBCL (and so probably for CMUCL and
> other implementations based on PCL), this restriction could probably
> be dropped. Note that the slot-xxx-using-class functions were changed
> very late in the game before the AMOP book got published.

There are many, many paths for optimizations of slot access in SBCL's
CLOS, and I honestly can't remember which of the paths caused this
issue.  Just to demonstrate some of the complexity, one path (which
probably does take advantage of this restriction on portable code) is
the optimization of full calls to slot-value, when no other
information is available to the compiler: something like

  (defun silly-example (object slotd)
    (funcall (foo) (class-of object) object slotd))
  (defun foo ()
    #'sb-mop:slot-value-using-class)

Here a call to SILLY-EXAMPLE is pretty much bound to do a full call to
slot-value-using-class.  The way this is currently implemented is
essentially

  (defmethod sb-mop:compute-discriminating-function 
      ((gf (eql #'sb-mop:slot-value-using-class)))
    (lambda (class object slotd)
      (funcall (slot-definition-reader-function slotd) object)))

where the reader-function slot of an effective slot definition holds a
closure which knows how to access the slot.  At first sight, this
seems bound to lose, because we've thrown away the class argument, and
there's only one closure for all instances of a given class; however,
the closure can actually be over quite a complicated set of actions.

In fact, the PCL code does close over a significant amount of
dispatch: so conforming MOP code such as

  (defclass foo-class (standard-class) ())
  (defmethod sb-mop:validate-superclass 
      ((class foo-class) (super standard-class))
    t)
  (defclass foo () (a)
    (:metaclass foo-class))
  (defvar *foo* (make-instance 'foo)) ; FOO is finalized by this line
  (slot-value *foo* 'a) ; slot-unbound
  (defmethod sb-mop:slot-value-using-class ((class foo-class) object slotd)
    3)
  (slot-value *foo* 'a) ; returns 3

actually does what it's meant to; the effective slot definition's
reader function is updated when the new method, applicable to objects
with the relevant metaclass, is defined.

At this point in this article, I was going to use

  (defclass bar () (a))
  (defmethod sb-mop:slot-value-using-class (class (object bar) slotd)
    4)
  (defvar *bar* (make-instance 'bar))
  (slot-value *bar* 'a)

as an example of MOP code which didn't work with SBCL's
implementation, but then I realized that in fact it is working as
designed when it signals SLOT-UNBOUND; this method is less specific
than the system method for SVUC, so it is quite right that it doesn't
get called.

So now I don't remember what the issue that Pascal and I discussed
was... Pascal, I encourage you to share more details if you can dredge
them up.

Christophe
From: Pascal Costanza
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <48kspgFkneifU1@individual.net>
Christophe Rhodes wrote:

> At this point in this article, I was going to use
> 
>   (defclass bar () (a))
>   (defmethod sb-mop:slot-value-using-class (class (object bar) slotd)
>     4)
>   (defvar *bar* (make-instance 'bar))
>   (slot-value *bar* 'a)
> 
> as an example of MOP code which didn't work with SBCL's
> implementation, but then I realized that in fact it is working as
> designed when it signals SLOT-UNBOUND; this method is less specific
> than the system method for SVUC, so it is quite right that it doesn't
> get called.
> 
> So now I don't remember what the issue that Pascal and I discussed
> was... Pascal, I encourage you to share more details if you can dredge
> them up.

The following is an example where specialization on the object argument 
doesn't work:

(defclass test-object ()
   ((slot :initform 42)))

(defmethod slot-value-using-class :after
   ((class standard-class)
    (object test-object)
    slot)
   (declare (ignore slot))
   (print "Accessed a test-object slot."))

Here is a session in SBCL that shows that the S-V-U-C method is not 
executed:

* (make-instance 'test-object)

#<TEST-OBJECT {11CE4099}>
* (slot-value * 'slot)

42

Whether or whether not such a method is executed can depend on even more 
subtle issues. Here is a session in OpenMCL, based on the same class and 
method definitions:

? (make-instance 'test-object)
#<TEST-OBJECT #x84A0E26>
? (slot-value * 'slot)

"Accessed a test-object slot."
42
? (let ((object (make-instance 'test-object)))
     (slot-value object 'slot))
42

Gary Byers told that the reason for the different behavior between the 
(slot-value * 'slot) and the (let ...) forms is that the code inside the 
let form is compiled and slot accesses in compiled code is optimized.

It would probably be helpful if the implementations that expect the 
class and/or the slot arguments to be specialized would signal a warning 
when that's not the case.


Pascal

-- 
3rd European Lisp Workshop
July 3-4 - Nantes, France - co-located with ECOOP 2006
http://lisp-ecoop06.bknr.net/
From: Alan Crowe
Subject: Re: intercepting (setf accessor)
Date: 
Message-ID: <86zmjmczq1.fsf@cawtech.freeserve.co.uk>
"Sacha" <··@address.spam> writes:

> Still got more newbie questions.
> This time it's more about architecture.
> 
> Let's say i have a class defined as follow :
> 
> (defclass item ()
>      ((parent :accessor parent)))
> 
> What i want is a way to intercept calls to (setf (parent item-instance) 
> parent-object)
> in order to validate the new value, or maybe update an index or a database,
> or whatever along those lines...
> 
> How would i go about doing this ?
> I'd rather avoid MOP if possible.
> That's why i don't really try intercepting (setf (slot-value item-instance 
> 'parent) parent-object)
> 
> I thought about making eql specialisers on the fly, maybe using a
> "closure builder" then releasing it when done with the item ...
> 
> but
> 1: i'm afraid this might be like using a cannon to kill a fly
> 2: i'm not quite sure how to do this
> 
> Also i don't really want to use a library that would do this
> for me, as the real goal is learning.
> 
> Any ideas would be greatly apreciated.
> 
> Sacha 

From the DEFCLASS page:

     The following slot options are available: 

          The :reader slot option specifies that an
          unqualified method is to be defined on the generic
          function named reader-function-name to read the
          value of the given slot.

          The :writer slot option specifies that an
          unqualified method is to be defined on the generic
          function named writer-function-name to write the
          value of the slot.

          The :accessor slot option specifies that an
          unqualified method is to be defined on the generic
          function named reader-function-name to read the
          value of the given slot and that an unqualified
          method is to be defined on the generic function
          named (setf reader-function-name) to be used with
          setf to modify the value of the slot.

:accessor is just an abbreviation for the common case in
which you want both the default reader and the default writer

You don't want the default :writer, don't ask for it.
Just write your own writer method.

CL-USER> (defclass even ()
           ((2n :reader 2n)))
#<STANDARD-CLASS EVEN {4898E675}>

CL-USER> (defmethod (setf 2n)(new-value (obj even))
           "Odd numbers are made smaller"
           (setf (slot-value obj '2n)
                 (* 2 (floor new-value 2))))
#<STANDARD-METHOD (SETF |2N|) (T EVEN) {48A2DD85}>

CL-USER> (defvar o (make-instance 'even))
O

CL-USER> (setf (2n o) 3)
2

CL-USER> (2n o)
2
--
Alan Crowe