From: Peter Seibel
Subject: (setf (getf ...))
Date: 
Message-ID: <m3n0909ama.fsf@javamonkey.com>
So I just noticed something in the dictionary entry for GETF that I
must have overlooked before and wanted to check that my new
interpretation is correct. The entry says:

  "setf of getf is permitted to either write the value of place
  itself, or modify of any part, car or cdr, of the list structure
  held by place."

From that I take it from that the following function will not
necessarily modify the plists contained in the list-of-plists passed
to it since the SETF might simply change the value of the local
variable plist.

  (defun change-values (list-of-plists)
    (dolist (plist list-of-plists)
      (setf (getf plist :foo) (random 10))))

In practice (in ACL anyway) when the elements of list-of-plists are
plists that already have a :foo entry, it *does* modify them. But
unless I'm missing something, there's no reason to rely on that
behavior. So am I missing anything?

-Peter

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

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

From: Erann Gat
Subject: Re: (setf (getf ...))
Date: 
Message-ID: <gNOSPAMat-0601041147240001@k-137-79-50-101.jpl.nasa.gov>
In article <··············@javamonkey.com>, Peter Seibel
<·····@javamonkey.com> wrote:

> So I just noticed something in the dictionary entry for GETF that I
> must have overlooked before and wanted to check that my new
> interpretation is correct. The entry says:
> 
>   "setf of getf is permitted to either write the value of place
>   itself, or modify of any part, car or cdr, of the list structure
>   held by place."
> 
> From that I take it from that the following function will not
> necessarily modify the plists contained in the list-of-plists passed
> to it since the SETF might simply change the value of the local
> variable plist.
> 
>   (defun change-values (list-of-plists)
>     (dolist (plist list-of-plists)
>       (setf (getf plist :foo) (random 10))))
> 
> In practice (in ACL anyway) when the elements of list-of-plists are
> plists that already have a :foo entry, it *does* modify them. But
> unless I'm missing something, there's no reason to rely on that
> behavior. So am I missing anything?

I interpret this to mean that e.g.:

(let ( (plist '(a b c d)) )
  (setf (getf plist 'a) 'q)
  plist)

is free to return either (a q c d) or (c d a q).

Or, perhaps more to the point:

(let ( (plist '(a b)) )
  (setf (getf plist 'q) 'z)
  plist)

could return either (a b q z) or (q z a b).

E.
From: Kaz Kylheku
Subject: Re: (setf (getf ...))
Date: 
Message-ID: <cf333042.0401061909.3e9ccf05@posting.google.com>
·········@jpl.nasa.gov (Erann Gat) wrote in message news:<··························@k-137-79-50-101.jpl.nasa.gov>...
> I interpret this to mean that e.g.:
> 
> (let ( (plist '(a b c d)) )
>   (setf (getf plist 'a) 'q)
>   plist)
>
> is free to return either (a q c d) or (c d a q).

It's also free to return:

  bus error (core dumped)
  shell$ 

or

  error: cannot store Q into CAR of read-only cell (A B C D).
  repl>

:)
From: Peter Seibel
Subject: Re: (setf (getf ...))
Date: 
Message-ID: <m3ekuc948o.fsf@javamonkey.com>
·········@jpl.nasa.gov (Erann Gat) writes:

> In article <··············@javamonkey.com>, Peter Seibel
> <·····@javamonkey.com> wrote:
> 
> > So I just noticed something in the dictionary entry for GETF that I
> > must have overlooked before and wanted to check that my new
> > interpretation is correct. The entry says:
> > 
> >   "setf of getf is permitted to either write the value of place
> >   itself, or modify of any part, car or cdr, of the list structure
> >   held by place."
> > 
> > From that I take it from that the following function will not
> > necessarily modify the plists contained in the list-of-plists passed
> > to it since the SETF might simply change the value of the local
> > variable plist.
> > 
> >   (defun change-values (list-of-plists)
> >     (dolist (plist list-of-plists)
> >       (setf (getf plist :foo) (random 10))))
> > 
> > In practice (in ACL anyway) when the elements of list-of-plists are
> > plists that already have a :foo entry, it *does* modify them. But
> > unless I'm missing something, there's no reason to rely on that
> > behavior. So am I missing anything?
> 
> I interpret this to mean that e.g.:
> 
> (let ( (plist '(a b c d)) )
>   (setf (getf plist 'a) 'q)
>   plist)
> 
> is free to return either (a q c d) or (c d a q).
> 
> Or, perhaps more to the point:
> 
> (let ( (plist '(a b)) )
>   (setf (getf plist 'q) 'z)
>   plist)
> 
> could return either (a b q z) or (q z a b).

Sure. But that's sort of orthogonal to my question. It's pretty clear
that the place (`plist' in this case) will have a correct value (i.e.
one that contains the correct value for the set indicator and all the
old indicator/value pairs after the SETF. But that value could be
created in any of a number of ways: by modifying the CAR of the cons
cell containing the current value (assuming the indicator is already
present); modifying the CDR of the cons cell preceeding the indicator
to splice in a new cons cell; modifying that same CDR to remove the
two cons cells containing the indicator and its value and then add two
new cons cell elsewhere in the plist; or even copying the whole plist
and changing the appropriate value while making the copy. Obviously
some of these may be "better" implementations by various metrics but I
think--now--that they would all be supported by the standard. Which
means I need to make some changes to Chapter 3 of my book which
incorrectly depends on a function similar to CHANGE-VALUES. Oops.

-Peter

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

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Kaz Kylheku
Subject: Re: (setf (getf ...))
Date: 
Message-ID: <cf333042.0401061443.12f960b5@posting.google.com>
Peter Seibel <·····@javamonkey.com> wrote in message news:<··············@javamonkey.com>...
> So I just noticed something in the dictionary entry for GETF that I
> must have overlooked before and wanted to check that my new
> interpretation is correct. The entry says:
> 
>   "setf of getf is permitted to either write the value of place
>   itself, or modify of any part, car or cdr, of the list structure
>   held by place."
> 
> From that I take it from that the following function will not
> necessarily modify the plists contained in the list-of-plists passed
> to it since the SETF might simply change the value of the local
> variable plist.
> 
>   (defun change-values (list-of-plists)
>     (dolist (plist list-of-plists)
>       (setf (getf plist :foo) (random 10))))

What if some of the plists are NIL? There is no way that the original
list-of-plists can be modified in that case since there is no cell to
boostrap from; a new plist will be constructed and bound to the local
variable.

> In practice (in ACL anyway) when the elements of list-of-plists are
> plists that already have a :foo entry, it *does* modify them. But
> unless I'm missing something, there's no reason to rely on that
> behavior. So am I missing anything?

No; the meaning of ``permitted'' is clear.

I think for this type of code, you really need a SYMBOL-MACROLET based
version of DOLIST which allows the list to be mutated via SETQ or SETF
of the iteration variable.

How about DOLISTF:

(defmacro dolistf ((sym list &optional result) &body forms)
  (let ((sublist-sym (gensym "SUBLIST-")))
    `(block nil
       (maplist (lambda (,sublist-sym)
                  (symbol-macrolet ((,sym (car ,sublist-sym)))
                    ,@forms))
                ,list)
       ,result)))

Now you can do funky stuff like:

  (defvar *list* (list 1 2 3))

  (dolistf (i *list* *list*) (incf i)) --> (2 3 4)

Now there is a truly imperative DOLIST that is understandable to Java,
C++, Perl and Python programmers. ;)

With this DOLISTF, your example works as intended, even if we start
with a list of empty plists:

  (defvar *list-of-plists* (list () () ()))

  (dolistf (plist *list-of-plists*)
    (setf (getf plist :foo) (random 10)))

  *list-of-plists* --> ((:FOO 5) (:FOO 4) (:FOO 7))

Oh yeah, and since this is implemented over MAPLIST, using a real
lexical variable, per-iteration lexical capture works right, too.
From: Barry Margolin
Subject: Re: (setf (getf ...))
Date: 
Message-ID: <barmar-C66A80.17060006012004@netnews.attbi.com>
In article <··············@javamonkey.com>,
 Peter Seibel <·····@javamonkey.com> wrote:

> So I just noticed something in the dictionary entry for GETF that I
> must have overlooked before and wanted to check that my new
> interpretation is correct. The entry says:
> 
>   "setf of getf is permitted to either write the value of place
>   itself, or modify of any part, car or cdr, of the list structure
>   held by place."
> 
> From that I take it from that the following function will not
> necessarily modify the plists contained in the list-of-plists passed
> to it since the SETF might simply change the value of the local
> variable plist.
> 
>   (defun change-values (list-of-plists)
>     (dolist (plist list-of-plists)
>       (setf (getf plist :foo) (random 10))))
> 
> In practice (in ACL anyway) when the elements of list-of-plists are
> plists that already have a :foo entry, it *does* modify them. But
> unless I'm missing something, there's no reason to rely on that
> behavior. So am I missing anything?

No, you've got it.  It has to be able to assign the variable in case the 
original value is NIL -- there's no cars or cdrs to modify.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
From: Drew McDermott
Subject: Re: (setf (getf ...))
Date: 
Message-ID: <bu2581$7sv$1@news.wss.yale.edu>
Barry Margolin wrote:
> In article <··············@javamonkey.com>,
>  Peter Seibel <·····@javamonkey.com> wrote:
> 
  >
> No, you've got it.  It has to be able to assign the variable in case the 
> original value is NIL -- there's no cars or cdrs to modify.
> 

So the right way to do Peter's task is

    (defun change-values (list-of-plists)
        (repeat :for  ((_ :in list-of-plists :tail pll))
           (setf (getf (car pll) ':foo)
                 (random 10))))

The 'repeat' macro construct (v :in l :tail w) steps 'v' through list 
'l', binding 'w' to successive tails (starting with 'v').  If 'v' isn't 
referenced, it can be written as '_'.  I assume there's a way to do the 
same thing with 'loop'.

-- 
                                    -- Drew McDermott
                                       Yale Computer Science Department
From: Peter Seibel
Subject: Re: (setf (getf ...))
Date: 
Message-ID: <m3zncrxd2n.fsf@javamonkey.com>
Drew McDermott <··················@at.yale.dot.edu> writes:

> Barry Margolin wrote:
> > In article <··············@javamonkey.com>,
> >  Peter Seibel <·····@javamonkey.com> wrote:
> >
>   >
> > No, you've got it.  It has to be able to assign the variable in case
> > the original value is NIL -- there's no cars or cdrs to modify.
> >
> 
> So the right way to do Peter's task is
> 
>     (defun change-values (list-of-plists)
>         (repeat :for  ((_ :in list-of-plists :tail pll))
>            (setf (getf (car pll) ':foo)
>                  (random 10))))

Aren't you still missing a SETF? I.e.

  (setf (car pll) (setf (getf (car pll) :foo) (random 10)))

instead of:

  (setf (getf (car pll) :foo) (random 10))

> The 'repeat' macro construct (v :in l :tail w) steps 'v' through list
> 'l', binding 'w' to successive tails (starting with 'v').  If 'v'
> isn't referenced, it can be written as '_'.  I assume there's a way to
> do the same thing with 'loop'.

With LOOP I guess that would be:

  (defun change-values (list-of-plists)
    (loop for e on list-of-plists do
      (setf (car e) (setf (getf (car e) :foo) (random 10)))))

Thanks for the insight, I hadn't thought of doing it that way.

-Peter

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

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Drew McDermott
Subject: Re: (setf (getf ...))
Date: 
Message-ID: <bu4gsu$b8o$1@news.wss.yale.edu>
>> [me]
>>So the right way to do Peter's task is
>>
>>    (defun change-values (list-of-plists)
>>        (repeat :for  ((_ :in list-of-plists :tail pll))
>>           (setf (getf (car pll) ':foo)
>>                 (random 10))))
> 
> [Peter Seibel]
> Aren't you still missing a SETF? I.e.
> 
>   (setf (car pll) (setf (getf (car pll) :foo) (random 10)))
> 
> instead of:
> 
>   (setf (getf (car pll) :foo) (random 10))

yt(4): (defvar pll1 '((:foo 0 :baz 0)
		      (:baz 0 :foo 0)
		      (:baz 0)))
pll1
yt(5): (change-values pll1)
nil
yt(6): pll1
((:foo 1 :baz 0) (:baz 0 :foo 0) (:foo 3 :baz 0))
yt(7):

The beauty of SETF is that it unravels its left-hand side in just the 
right way.

You could write
    (incf (getf (car pll) ':foo))

and the right thing would happen, too.  (Although I'd rather write
    (!= (get (car pll) ':foo) (+ *-* 1)), where != is like SETF except 
that on the right-hand side '*-*' is bound to the value of the left-hand 
side, again using the correct amount of unraveling so that nothing is 
evaluated more than once.)

     -- Drew McDermott
From: Peter Seibel
Subject: Re: (setf (getf ...))
Date: 
Message-ID: <m3ad4qvzv4.fsf@javamonkey.com>
Drew McDermott <··················@at.yale.dot.edu> writes:

> >> [me]
> >>So the right way to do Peter's task is
> >>
> >>    (defun change-values (list-of-plists)
> >>        (repeat :for  ((_ :in list-of-plists :tail pll))
> >>           (setf (getf (car pll) ':foo)
> >>                 (random 10))))
> > [Peter Seibel]
> > Aren't you still missing a SETF? I.e.
> >   (setf (car pll) (setf (getf (car pll) :foo) (random 10)))
> > instead of:
> >   (setf (getf (car pll) :foo) (random 10))
> 
> yt(4): (defvar pll1 '((:foo 0 :baz 0)
> 		      (:baz 0 :foo 0)
> 		      (:baz 0)))
> pll1
> yt(5): (change-values pll1)
> nil
> yt(6): pll1
> ((:foo 1 :baz 0) (:baz 0 :foo 0) (:foo 3 :baz 0))
> yt(7):
> 
> The beauty of SETF is that it unravels its left-hand side in just
> the right way.

Ah, yes. In the case of GETF, LDB, and MASK-FIELD. I forgot about
that.

-Peter

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

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Pascal Costanza
Subject: Re: (setf (getf ...))
Date: 
Message-ID: <btgufq$pse$1@f1node01.rhrz.uni-bonn.de>
Peter Seibel wrote:

>   (defun change-values (list-of-plists)
>     (dolist (plist list-of-plists)
>       (setf (getf plist :foo) (random 10))))

What's wrong with this?

(defun change-values (list-of-plists)
   (mapcar (lambda (plist)
             (list* :foo (random 10) plist))
           list-of-plists))

and then:

(setf a-list-of-plists (change-values a-list-of-plists))

or additionally:

(define-modify-macro change-values-f ()
   change-values)

so then:

(change-value-f a-list-of-plists)

If you need more efficiency, you can achieve it by using MAPCAN in 
certain cases, and maybe provide another version of LIST* that modifies 
an already existing :FOO property.

LIST* does the right thing because GETF is politically correct, i.e. it 
prefers left entries over right entries. Since property list should 
usually be short, the overhead shouldn't be too high. You could also 
achieve a better average performance by only compressing property lists 
that grow beyond a certain threshold.

The usual rule applies that you shouldn't prematurely optimize things 
that you haven't profiled before.


All the best,
Pascal

-- 
Pascal Costanza               University of Bonn
···············@web.de        Institute of Computer Science III
http://www.pascalcostanza.de  R�merstr. 164, D-53117 Bonn (Germany)