From: proton
Subject: CLOS: Triggering a method when a slot is modified
Date: 
Message-ID: <8cd69e22-0add-4cb8-8250-bd4d1bcb5d1d@i12g2000prf.googlegroups.com>
Is there a way to have a method triggered automatically whenever a
class slot is modified?

I have two slots which must maintain a certain relation between them,
therefore if one is changed the other one must be updated accordingly.
The obvious solution is to use

(defmethod modify-slot1 (new-value)
   (setf (slot1 class) new-value)
   (setf (slot2 class) (f new-value)))

and the same with slot2.

However, this solution does not look very elegant, and it does not
protect the slots from an accidental (setf (slot1 class) new-value)
elsewhere in the code.

Is there a better way to do this?
TIA

From: Pillsy
Subject: Re: CLOS: Triggering a method when a slot is modified
Date: 
Message-ID: <5d740afe-f80f-4d11-92b3-2f684b24048d@t1g2000pra.googlegroups.com>
On Dec 14, 4:09 pm, proton <··········@gmail.com> wrote:

> I have two slots which must maintain a certain relation between them,
> therefore if one is changed the other one must be updated accordingly.
> The obvious solution is to use

> (defmethod modify-slot1 (new-value)
>    (setf (slot1 class) new-value)
>    (setf (slot2 class) (f new-value)))

> and the same with slot2.

Well, you can do this more cleanly by using an :AFTER method on your
accessor, like so:

(defclass a-class ()
  ((slot-1 :accessor slot-1)
   (slot-2 :accessor slot-2)))

(defmethod (setf slot-1) :after (new-value (object class-1))
  (setf (slot-2 object) (do-stuff new-value)))

Cheers,
Pillsy
From: Ken Tilton
Subject: Re: CLOS: Triggering a method when a slot is modified
Date: 
Message-ID: <4762fd87$0$31168$607ed4bc@cv.net>
proton wrote:
> Is there a way to have a method triggered automatically whenever a
> class slot is modified?
> 
> I have two slots which must maintain a certain relation between them,
> therefore if one is changed the other one must be updated accordingly.
> The obvious solution is to use
> 
> (defmethod modify-slot1 (new-value)
>    (setf (slot1 class) new-value)
>    (setf (slot2 class) (f new-value)))
> 
> and the same with slot2.

Both ways? Could get tricky with my Cells package, depending on whether 
the update to 2 leads to an update to 1 to an update to 2 and never 
ends. As Alex suggested you can write your own writer method, but then 
you still need to worry about cycling endlessly back and forth.

kt
-- 
http://www.theoryyalgebra.com/

"In the morning, hear the Way;
  in the evening, die content!"
                     -- Confucius
From: Pascal Costanza
Subject: Re: CLOS: Triggering a method when a slot is modified
Date: 
Message-ID: <5sgh29F18vtseU2@mid.individual.net>
Ken Tilton wrote:
> 
> 
> proton wrote:
>> Is there a way to have a method triggered automatically whenever a
>> class slot is modified?
>>
>> I have two slots which must maintain a certain relation between them,
>> therefore if one is changed the other one must be updated accordingly.
>> The obvious solution is to use
>>
>> (defmethod modify-slot1 (new-value)
>>    (setf (slot1 class) new-value)
>>    (setf (slot2 class) (f new-value)))
>>
>> and the same with slot2.
> 
> Both ways? Could get tricky with my Cells package, depending on whether 
> the update to 2 leads to an update to 1 to an update to 2 and never 
> ends. As Alex suggested you can write your own writer method, but then 
> you still need to worry about cycling endlessly back and forth.

You can check whether the value of one slot actually changes, and only 
trigger an update of the other slot if that's the case.


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Alex Mizrahi
Subject: Re: CLOS: Triggering a method when a slot is modified
Date: 
Message-ID: <4762f4e9$0$90266$14726298@news.sunsite.dk>
 p> However, this solution does not look very elegant, and it does not
 p> protect the slots from an accidental (setf (slot1 class) new-value)
 p> elsewhere in the code.

slot1 is accessor -- it's a generic function. thus you can defmethod your 
custom code. i think you need something like:

(defmethod (setf slot1) :after (new-value (object your-class))
  (setf (slot-value object 'slot2) (compute-slot2-from-slot1 new-value))

certainly users (and you) can still bypass this mechanizm via slot-value, 
and we can hook this too, but i think there's no need.

there are other options too: if you can compute slot2 from slot1, you can 
just not have such slot, but two methods:

(defmethod slot2 ((object your-class)) ...)
(defmethod (setf slot2)) (new-value (object your-class)) ...)

and "emulate" this slot2.

and if having related slots happens more than once in your applications, 
check Cells (http://common-lisp.net/project/cells/)/ 
From: Ken Tilton
Subject: Re: CLOS: Triggering a method when a slot is modified
Date: 
Message-ID: <4762f7c8$0$31136$607ed4bc@cv.net>
Alex Mizrahi wrote:
>  p> However, this solution does not look very elegant, and it does not
>  p> protect the slots from an accidental (setf (slot1 class) new-value)
>  p> elsewhere in the code.
> 
> slot1 is accessor -- it's a generic function. thus you can defmethod your 
> custom code. i think you need something like:
> 
> (defmethod (setf slot1) :after (new-value (object your-class))
>   (setf (slot-value object 'slot2) (compute-slot2-from-slot1 new-value))
> 
> certainly users (and you) can still bypass this mechanizm via slot-value, 
> and we can hook this too, but i think there's no need.
> 
> there are other options too: if you can compute slot2 from slot1, you can 
> just not have such slot, but two methods:
> 
> (defmethod slot2 ((object your-class)) ...)
> (defmethod (setf slot2)) (new-value (object your-class)) ...)
> 
> and "emulate" this slot2.
> 
> and if having related slots happens more than once in your applications, 
> check Cells (http://common-lisp.net/project/cells/)/ 
> 
> 

I think we better start sending people here, all the links on that page 
are useless (unless one points here <g>):

   http://common-lisp.net/cgi-bin/viewcvs.cgi/cells/?root=cells

kt

-- 
http://www.theoryyalgebra.com/

"In the morning, hear the Way;
  in the evening, die content!"
                     -- Confucius
From: Pascal Costanza
Subject: Re: CLOS: Triggering a method when a slot is modified
Date: 
Message-ID: <5sgh0mF18vtseU1@mid.individual.net>
proton wrote:
> Is there a way to have a method triggered automatically whenever a
> class slot is modified?
> 
> I have two slots which must maintain a certain relation between them,
> therefore if one is changed the other one must be updated accordingly.
> The obvious solution is to use
> 
> (defmethod modify-slot1 (new-value)
>    (setf (slot1 class) new-value)
>    (setf (slot2 class) (f new-value)))
> 
> and the same with slot2.
> 
> However, this solution does not look very elegant, and it does not
> protect the slots from an accidental (setf (slot1 class) new-value)
> elsewhere in the code.
> 
> Is there a better way to do this?

1) Use accessors and define :after methods.

2) Define your own metaclass, and define an :after method on (setf 
slot-value-using-class).


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/