From: Peter Seibel
Subject: Using methods on (setf foo)?
Date: 
Message-ID: <m3sm89t1c3.fsf@javamonkey.com>
Suppose I have a user-defined class with some slots:

  (defclass playlist ()
    ((songs    :accessor songs    :initform ()))
    ((shuffle  :accessor shuffle  :initform :none))
    ((ordering :accesser ordering :initform :song)))

Now suppose I want to provide an API whereby clients of this class can
set SHUFFLE and ORDERING while at the same time actually sorting or
shuffling SONGS. Is it better to do this:

  (defmethod set-shuffle ((playlist playlist) shuffle)
    (setf (shuffle playlist) shuffle)
    (shuffle-songs playlist))

  (defmethod set-ordering ((playlist playlist) ordering)
    (setf (ordering playlist) ordering)
    (sort-songs playlist))

or this:

  (defmethod (setf shuffle) :after (shuffle (playlist playlist))
    (declare (ignore shuffle))
    (shuffle-songs playlist))

  (defmethod (setf ordering) (ordering (playlist playlist))
    (declare (ignore ordering))
    (sort-songs playlist))

On the one hand, it seems silly to define a set-foo GF and method when
there's already a perfectly good (setf foo) combo waiting to be
extended. On the other hand, mightn't it violate the principle of
least astonishment to have a simple "assignment" have such dramatic
side effects?

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp

From: Kenny Tilton
Subject: Re: Using methods on (setf foo)?
Date: 
Message-ID: <KUCdd.173440$4h7.31984974@twister.nyc.rr.com>
Peter Seibel wrote:
> Suppose I have a user-defined class with some slots:
> 
>   (defclass playlist ()
>     ((songs    :accessor songs    :initform ()))
>     ((shuffle  :accessor shuffle  :initform :none))
>     ((ordering :accesser ordering :initform :song)))
> 
> Now suppose I want to provide an API whereby clients of this class can
> set SHUFFLE and ORDERING while at the same time actually sorting or
> shuffling SONGS. Is it better to do this:
> 
>   (defmethod set-shuffle ((playlist playlist) shuffle)
>     (setf (shuffle playlist) shuffle)
>     (shuffle-songs playlist))
> 
>   (defmethod set-ordering ((playlist playlist) ordering)
>     (setf (ordering playlist) ordering)
>     (sort-songs playlist))
> 
> or this:
> 
>   (defmethod (setf shuffle) :after (shuffle (playlist playlist))
>     (declare (ignore shuffle))
>     (shuffle-songs playlist))
> 
>   (defmethod (setf ordering) (ordering (playlist playlist))
>     (declare (ignore ordering))
>     (sort-songs playlist))
> 
> On the one hand, it seems silly to define a set-foo GF and method when
> there's already a perfectly good (setf foo) combo waiting to be
> extended. On the other hand, mightn't it violate the principle of
> least astonishment to have a simple "assignment" have such dramatic
> side effects?

I think it is OK to automatically handling requisite housekeeping off 
some slot state change in a setter on that slot. But here we are not 
talking about housekeeping, we are talking about core functionality 
being kicked off by a simple state change. Reminds me of Cells, btw.

I think this example does not "work" because, based on the little code 
shown here, it is not clear why the shuffle slot even exists. If you 
want the long-lived state so you can indicate to the user in some 
interface by what parameter the songs are sorted, I would instead simply 
have an API call to shuffle songs and have /that/ set the shuffle slot 
as a sort of log of the activity. Then there is no surprise.

Of course, if one is using Cells one is accustomed to, nay, addicted to 
having slot state changes propagating automatically to any dependencies 
anywhere in the application. But I digress. :)

kenny

-- 
Cells? Cello? Celtik?: http://www.common-lisp.net/project/cells/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
From: Pascal Costanza
Subject: Re: Using methods on (setf foo)?
Date: 
Message-ID: <cl700m$pso$1@newsreader2.netcologne.de>
Peter Seibel wrote:

>   (defmethod (setf shuffle) :after (shuffle (playlist playlist))
>     (declare (ignore shuffle))
>     (shuffle-songs playlist))
> 
>   (defmethod (setf ordering) (ordering (playlist playlist))
>     (declare (ignore ordering))
>     (sort-songs playlist))
> 
> On the one hand, it seems silly to define a set-foo GF and method when
> there's already a perfectly good (setf foo) combo waiting to be
> extended. On the other hand, mightn't it violate the principle of
> least astonishment to have a simple "assignment" have such dramatic
> side effects?

I'd prefer specializing existing setf methods. In case the side effects 
are really dramatic, the setters could just set a dirty flag and the 
songs getter could check that flag.

BTW, since I am dealing a lot with the MOP recently, the design 
philosophy there is that setters and getters are generally cheap in that 
they just report or set the state while expensive changes are handled 
through reinitialize-instance. This also seems to be a good idea. 
(Following that design philosophy, you would only have readers in your 
case.)


Pascal

-- 
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
From: Bruno Haible
Subject: Re: Using methods on (setf foo)?
Date: 
Message-ID: <cl8kkr$mee$1@laposte.ilog.fr>
Peter Seibel <·····@javamonkey.com> asked:
>
> On the other hand, mightn't it violate the principle of
> least astonishment to have a simple "assignment" have such dramatic
> side effects?

No, that's essentially why the accessor methods are part of generic
functions: So that programmers can add bookkeeping updates.

People who want just the "simple assignment" can use
  (setf (slot-value playlist 'songs) ...)
as a last resort.

                   Bruno