Kaz Kylheku
5/13/2015 11:46:00 PM
On 2015-05-13, Pascal J. Bourguignon <pjb@informatimago.com> wrote:
> Kaz Kylheku <kaz@kylheku.com> writes:
>
>> On 2015-05-13, Kaz Kylheku <kaz@kylheku.com> wrote:
>>> According to the ANSI/CLHS description of PSETF, "the values in place1 through
>>> placen are read and saved".
>>
>> Sorry, the above text appears under SHIFTF, which is what this discussion
>> is intended to be about.
>
> It also says:
>
> For information about the evaluation of subforms of places, see
> Section 5.1.1.1 (Evaluation of Subforms to Places).
>
> which specifies left-to-right evaluation of the subforms as in psetf.
Thanks for investigating, Pascal.
Unfortunately, evaluation of the subforms of places doesn't establish
the timing of when their values are accessed---which need not happen
at all. For instance in (setf x y), the prior value of x is not accessed.
I'm not questioning evaluation order at all. Given
(shiftf a b c d e ... v)
it is clear that everything is evaluated left to right. For a place
to be evaluated, it means that the location to which it refers is
determined.
When evaluating the above shiftf, when do we retrieve and save the
old value of place a? We can do it immediately after evaluating a
(determining the location), before evaluating b. Or we can defer this
access after b is evaluated, or even after c, and so on.
One extreme is that the entire form is evaluated, and then places are accessed.
The other is that each place is accessed as soon as it is determined.
> Notably:
>
> push, pushnew, remf, incf, decf, shiftf, rotatef, psetf, pop
> evaluate all subforms before modifying any of the place
> locations.
>
> This leads to unambiguous operation:
>
> clall -r '(let ((x (list 1))
> (y (list 2))
> (z (list 3)))
> (shiftf (car (prog1 x (setf (car y) 0)))
> (car (prog1 y (setf (car z) 0)))
> (car (prog1 z)))
> (values x y z))'
Alas, yes, here, the behavior is clear, because each of your embedded side
effects refers to a *future* place (lexically to the right) that is not yet
evaluated at all, let alone accessed!
So as SHIFTF is evaluating the leftmost place
(car (prog1 x (setf (car y) 0)))
it hits a side effect in the form of the embedded (setf (car y) ...).
This side effect of course completes as part of the evaluation of this
form. So by the time the affected place is considered for the very first
time, its value is settled as zero.
The issue that I'm concerned with specifically affects the situation
that a SHIFTF place contains side effects within its subforms, and those
side effects affect another place *which lies to the left*, and which
has therefore already been evaluated (reduced to a determinant of
its location).
In other words, if you have a spare moment, please try this with your "clall":
clall -r '(let ((x (list 1))
(y (list 2))
(z (list 3)))
(shiftf (car (prog1 x))
(car (prog1 y (setf (car x) 0)))
(car (prog1 z (setf (car y) 0)))
;; One more form: clobber z for good measure, too:
(prog1 42 (setf (car z) 0))
(values x y z))'
Now it matters whether the value of (car (prog1 x)) is accessed and saved
immediately after that place is evaluated, or whether this access is
deferred. If it immediate, it will access 1; whereas if it is deferred
past the evaluation of the (car (prog1 y ...)) place, then it will access zero.