From: Joshua Taylor
Subject: PUSH and VALUES bugs?
Date: 
Message-ID: <9eba14b7-ad7c-4c04-bad3-fbce9b89c3bb@e20g2000vbc.googlegroups.com>
Hi all. I've been learning about modifying macros, and as an exercise,
trying to re-implement PUSH. I could define XCONS (like CONS but with
args reversed) and then use DEFINE-MODIFY-MACRO to define XCONSF, but
this results in something like PUSH, but with the list as the first
argument, and the item second. OK, now on to a version with the
correct argument order.  Since we want to avoid multiple evaluation,
&c. we'll use GET-SETF-EXPANSION in defining MY-PUSH.

(defmacro my-push (obj place &environment environment)
  (let ((o (gensym (string '#:obj-))))
    (multiple-value-bind (vars vals store-vals writer-form reader-
form)
        (get-setf-expansion place environment)
      `(let* ((,o ,obj) ,@(mapcar 'list vars vals))
         (multiple-value-bind ,store-vals
             (cons ,o ,reader-form)
           ,writer-form)))))

(let ((l (list 1 2 3))) (my-push 0 l) l)
;; => (0 1 2 3)

It seems to work correctly, and I think I'm doing this in the right
way. Now, the reason for

         (multiple-value-bind ,store-vals
             (cons ,o ,reader-form)

as opposed just binding the first store-val to the result of
(cons ...) is that the writer form will reference all of the store
variables.  This comes up in the case if place is a VALUES form,
described in [1], which says,

""A values form can be used as a place, provided that each of its
subforms is also a place form.  A form such as (setf (values
place-1 ...place-n) values-form) does the following:
1. The subforms of each nested place are evaluated in left-to-right
order.
2. The values-form is evaluated, and the first store variable from
each place is bound to its return values as if by multiple-value-bind.
3. If the setf expansion for any place involves more than one store
variable, then the additional store variables are bound to nil.
4. The storing forms for each place are evaluated in left-to-right
order. ""

Based on this, I'd think that the following output is correct

(let ((l (list 1 2 3))
      (x 'some-value-of-x))
  (my-push 0 (values l x))
  (list l x))
;; => ((0 1 2 3) NIL) ;; l has 0 pushed on, x is now NIL.

since GET-SETF-EXPANSION returns a list of two store-vars (one for l,
and one for x), and they're bound using MULTIPLE-VALUE-BIND with
values coming from CONS, which produces only one value, so the store-
var for x is bound to NIL, so l is pushed onto, and x is set to NIL.

I would have expected PUSH to work this way as well, especially given
the Note in the CLHS [2]: ""The effect of (push item place) is
equivalent to (setf place (cons item place)) except that the subforms
of place are evaluated only once, and item is evaluated before
place."" and given that

(let ((l (list 1 2 3))
      (x 'some-value-of-x))
  (setf (values l x) (cons 0 (values l x)))
  (list l x))
;; => ((0 1 2 3) NIL)

However look what happens in several implementations when we give
(let ((l (list 1 2 3)) (x 'some-value))
   (push 0 (values l x)))

* LispWorks:
Error: Wrong number of store variables (#:G1407 #:G1408) returned to
GET-SETF-METHOD for (VALUES L X).

* SBCL:
debugger invoked on a SB-INT:COMPILED-PROGRAM-ERROR: Execution of a
form compiled with errors.
Form: (PUSH 0 (VALUES L X))
Compile-time error: (in macroexpansion of (PUSH 0 (VALUES L X)))
(hint: For more precise location, try *BREAK-ON-SIGNALS*.)
GET-SETF-METHOD used for a form with multiple store variables:
  (VALUES L X)

* Clozure Common Lisp:
> Error: Multiple store variables not expected in setf expansion of (VALUES L X)
> While executing: (:INTERNAL CCL::NX1-COMPILE-LAMBDA), in process listener(1).

* CLISP (not an error, but unexpected nonetheless)
(0 1 2 3) ;
(NIL . SOME-VALUE)

Is there something I'm missing that leads to all these errors, or
something implementation-dependent-behavior that I've just missed?
Perhaps the LW and SBCL messages about GET-SETF-METHOD indicate
pre-"VALUES forms as places" implementations? Should I be doing
something differently, or filing some bug reports? All feedback
welcome. Thanks in advance!
//JT

[1] http://www.lispworks.com/documentation/HyperSpec/Body/05_abc.htm
[2] http://www.lispworks.com/documentation/HyperSpec/Body/m_push.htm

From: Tobias C. Rittweiler
Subject: Re: PUSH and VALUES bugs?
Date: 
Message-ID: <87vdodhqsp.fsf@freebits.de>
Joshua Taylor <···········@gmail.com> writes:

> Is there something I'm missing that leads to all these errors, or
> something implementation-dependent-behavior that I've just missed?
> Perhaps the LW and SBCL messages about GET-SETF-METHOD indicate
> pre-"VALUES forms as places" implementations? Should I be doing
> something differently, or filing some bug reports? All feedback
> welcome. Thanks in advance!

Contrast the specification of PUSH, or INCF, with the specifications of
ROTATEF, SHIFTF, or ASSERT. The latter explicitly mention "values",
whereas the former only talk about "value of place".

So basically it's just what the specification says.

  -T.

PS.

The CLHS entry of SETF just talks about "value of place", too, but I'm
afraid that's just a typographic glitch in that case.
From: Joshua Taylor
Subject: Re: PUSH and VALUES bugs?
Date: 
Message-ID: <f5066975-a35f-46c5-ad8f-c73250e1d5ba@t10g2000vbg.googlegroups.com>
On May 7, 11:32 am, "Tobias C. Rittweiler" <····@freebits.de.invalid>
wrote:
> Joshua Taylor <···········@gmail.com> writes:
> > Is there something I'm missing that leads to all these errors, or
> > something implementation-dependent-behavior that I've just missed?
> > Perhaps the LW and SBCL messages about GET-SETF-METHOD indicate
> > pre-"VALUES forms as places" implementations? Should I be doing
> > something differently, or filing some bug reports? All feedback
> > welcome. Thanks in advance!
>
> Contrast the specification of PUSH, or INCF, with the specifications of
> ROTATEF, SHIFTF, or ASSERT. The latter explicitly mention "values",
> whereas the former only talk about "value of place".
>
> So basically it's just what the specification says.
>   -T.
>
> PS.
> The CLHS entry of SETF just talks about "value of place", too, but I'm
> afraid that's just a typographic glitch in that case.

Thanks for the response!

I'm not sure I follow exactly what you're saying.  I do see what you
mean about comparing, e.g., "incf and decf are used for incrementing
and decrementing the value of place, respectively. " (from INCF) to
"If a place produces more values than there are store variables, the
extra values are ignored. If a place produces fewer values than there
are store variables, the missing values are set to nil. " from
ROTATEF.

My point was more along the lines that, e.g., from INCF:
"incf place [delta-form] => new-value
...
place---a place"
and that following glossary reference to place gives:
"place n. 1. a form which is suitable for use as a generalized
reference. 2. the conceptual location referred to by such a place."
And, from "5.1.2.3 VALUES forms as places" [1] :
"A values form can be used as a place, provided that each of its
subforms is also a place form."

I still think it sounds like (push 'foo (values x y)) should be valid,
since PUSH requires and item and a place, and (values x y) is a values
form, and a values form can be used as a place (and its behavior is
well defined).

That said, this seems like enough of a corner case that I'm not going
to pursue it any farther. The main drawback, it seems to me, is that
it means that there are a number of macros that ask for places that
don't actually work for a number of places. This is mildly irritating
when encountered in the implementation, and it means that user code
that expands to library functions inherits the same caveats.

But, as I said, I'm not going to pursue it anymore.

Cheers, //JT

[1] http://www.lispworks.com/documentation/HyperSpec/Body/05_abc.htm