From: Sam Steingold
Subject: defsetf with multiple values
Date: 
Message-ID: <ur7ii5eny.fsf@gnu.org>
SETF must return all values from the last storing form.
how can DEFSETF know how many values were passed?
E.g.,

(defstruct foo x y)
(setq z (make-foo))
(defsetf foo-both (foo) (x y)
  (let ((fv (gensym "FOO")) (xv (gensym "X")) (yv (gensym "Y")))
    `(let ((,fv ,foo) (,xv ,x) (,yv ,y))
       (setf (foo-x ,fv) ,xv
             (foo-y ,fv) ,yv)
       (values ,xv ,yv))))

then

(setf (foo-both z) (values 1 2))

will correctly return 2 values (1 and 2) and make Z evaluate to
#S(FOO :X 1 :Y 2)
but

(setf (foo-both z) 10)

will return 2 values (10 and NIL) instead of one value 10.
(it does modify the slots of Z correctly: #S(FOO :X 10 :Y NIL)).

So, how do I modify FOO-BOTH to return the right number of values?


-- 
Sam Steingold (http://www.podval.org/~sds) running w2k
<http://www.iris.org.il> <http://www.camera.org> <http://www.memri.org/>
<http://www.honestreporting.com> <http://www.mideasttruth.com/>
When you are arguing with an idiot, your opponent is doing the same.

From: Pascal Bourguignon
Subject: Re: defsetf with multiple values
Date: 
Message-ID: <878y4qhxfi.fsf@thalassa.informatimago.com>
Sam Steingold <···@gnu.org> writes:

> SETF must return all values from the last storing form.
> how can DEFSETF know how many values were passed?
> E.g.,
> 
> (defstruct foo x y)
> (setq z (make-foo))
> (defsetf foo-both (foo) (x y)
>   (let ((fv (gensym "FOO")) (xv (gensym "X")) (yv (gensym "Y")))
>     `(let ((,fv ,foo) (,xv ,x) (,yv ,y))
>        (setf (foo-x ,fv) ,xv
>              (foo-y ,fv) ,yv)
>        (values ,xv ,yv))))
> 
> then
> 
> (setf (foo-both z) (values 1 2))
> 
> will correctly return 2 values (1 and 2) and make Z evaluate to
> #S(FOO :X 1 :Y 2)
> but
> 
> (setf (foo-both z) 10)
> 
> will return 2 values (10 and NIL) instead of one value 10.
> (it does modify the slots of Z correctly: #S(FOO :X 10 :Y NIL)).
> 
> So, how do I modify FOO-BOTH to return the right number of values?

You can't with defsetf because it takes store-variables that are mere
symbols, not a (var [init-form [supplied-p-parameter]]).

In practice, you could test for NIL, but of course, you won't
distinguish between (values 1 nil) and (values 1).

On the other hand, it looks like it'd be possible with
define-setf-expander since it takes a macro lambda list:

(define-setf-expander foo-both (&optional (x nil x-p) (y nil y-p)
                                &environment env)
    ...)



-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
I need a new toy.
Tail of black dog keeps good time.
Pounce! Good dog! Good dog!
From: Kalle Olavi Niemitalo
Subject: Re: defsetf with multiple values
Date: 
Message-ID: <87u0ndkgje.fsf@Astalo.kon.iki.fi>
Pascal Bourguignon <····@mouse-potato.com> writes:

> On the other hand, it looks like it'd be possible with
> define-setf-expander since it takes a macro lambda list:
>
> (define-setf-expander foo-both (&optional (x nil x-p) (y nil y-p)
>                                 &environment env)
>     ...)

That lambda list is for subforms of the place form, not for store
variables.
From: Kalle Olavi Niemitalo
Subject: Re: defsetf with multiple values
Date: 
Message-ID: <87wts9kglh.fsf@Astalo.kon.iki.fi>
Sam Steingold <···@gnu.org> writes:

> (setf (foo-both z) 10)
>
> will return 2 values (10 and NIL) instead of one value 10.
> (it does modify the slots of Z correctly: #S(FOO :X 10 :Y NIL)).
>
> So, how do I modify FOO-BOTH to return the right number of values?

Two is the right number.  (get-setf-expansion '(foo-both z))
returns a setf expansion that includes a list of store variables
and a storing form that must return the values of those
variables, "which are the correct values for setf to return"
(section 5.1.1.2).  The setf expander that generates the list is
not told whether it was invoked for (setf (foo-both z) (values 1
2)) or (setf (foo-both z) 10).  Therefore, the number of store
variables will be the same in both cases, and so will the number
of returned values.
From: Barry Fishman
Subject: Re: defsetf with multiple values
Date: 
Message-ID: <m3d5tymiwy.fsf@ecube.site>
Sam Steingold <···@gnu.org> writes:
> (defstruct foo x y)
> (setq z (make-foo))
> (defsetf foo-both (foo) (x y)
>   (let ((fv (gensym "FOO")) (xv (gensym "X")) (yv (gensym "Y")))
>     `(let ((,fv ,foo) (,xv ,x) (,yv ,y))
>        (setf (foo-x ,fv) ,xv
>              (foo-y ,fv) ,yv)
>        (values ,xv ,yv))))

I'm a big confused.  According to the hyperspec, for the long form of
defsetf:

  During the evaluation of the FORMS, the variables in the LAMBDA-LIST
  and the STORE-VARIABLES are bound to names of temporary variables,
  generated as if by ‘gensym’ or ‘gentemp’, that will be bound by the
  expansion of ‘setf’ to the values of those subforms.  This binding
  permits the FORMS to be written without regard for order-of-evaluation
  issues.  ‘defsetf’ arranges for the temporary variables to be optimized
  out of the final result in cases where that is possible.

So wouldn't:

(defsetf foo-both (foo) (x y)
  `(progn
     (setf (foo-x ,foo) ,x
           (foo-y ,foo) ,y)
     (values ,x ,y)))

be sufficient?

--
Barry Fishman