[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Issue: SETF-METHOD-FOR-SYMBOLS (version 2)



This is not for release. I think I tend to agree with Scott that I'd
rather see a little more shuffling in GETF and LDB and less in SETF of
symbols. Hopefully, SETF-FUNCTION-VS-MACRO would reduce the amount of
hair that was involved for users of setf, anyway. 

!

Issue:         SETF-METHOD-FOR-SYMBOLS

References:    CLtL pp. 105, 99. Issue: PUSH-EVALUATION-ORDER.

Category:      CHANGE

Edit history:  Version 1   Moon 21 Sep 87
               Version 2 Masinter 23-Oct-87


Problem description:

The description of SETF in CLtL is inconsistent in that page 99
requires side-effects to be elaborated in left-to-right order,
while page 105 specifies a setf-method for symbols that makes
it impossible to implement this in some cases, such as the test
case given below (provided by Timothy Daly of IBM).

Proposal: SETF-METHOD-FOR-SYMBOLS:TEMPORARY-VARIABLE

Change the example of the result of

(GET-SETF-METHOD 'FOO) from
NIL NIL (#:G1174) (SETQ FOO #:G1174) FOO

to return, for example,

(#:G1175) (FOO) (#:G1174) (SETQ FOO #:G1174) #:G1175

Test Case:

Test Case (A): Given

(LET* ((R (LIST 'A 1 'B 2 'C 3))
       (S R))
  (SETF (GETF R 'B) (PROGN (SETQ R NIL) 6))
  (VALUES R S))

If side-effects are elaborated in left-to-right order,
the setq of R to NIL should not affect the result, since
it occurs after R is read and before R is written, and
therefore the value of both R and S should be (A 1 B 6 C 3).

A typical result in an implementation that believes CLtL p.105
more than CLtL p.99 is R = (B 6) and S = (A 1 B 2 C 3).

Test Case B:

(LET((A 0))
   (INCF (LDB (BYTE 2 2) A) (SETQ A 2))
   A)

Does this return 8, 10, or 2? If p. 99's description of order of
evaluation is correct, this should return 8.

Rationale:

The general principle mentioned on p.99 should override the
specific example on p.105.  The latter is probably just a mistake.

Current practice:

Symbolics and Lucid return the incorrect result mentioned in the test
case A. (Symbolics plans to fix this in the next release.) Franz and
Xerox returns something else: R = nil and S = (a 1 b 6 c 3). HP Common
Lisp produces the recommended value. Xerox Common Lisp returns A=10 in
Test Case B.

Spice Lisp returns the recommended value for the test case A, even
though it uses the suggested value for the setf-method for symbols,
because the get-setf-method for GETF introduces additional temporary
bindings.

Adoption Cost:

SETF is an intricate part of Common Lisp, and the fact that not all
implementations currently return the same thing indicates that some
care might be required in updating implementations.  However, in
some implementations changing what get-setf-method returns when its
argument is a symbol is the only change required.

It's been pointed out that this change might cause less efficient code
to be produced in some cases, since setf methods will involve more
temporary variables, however Moon believes that the optimizations are
not difficult and probably are already done by most implementations.

Cost of non-adoption:

Users will think SETF is complicated and hard to understand, because
implementations won't conform to a simple general principle that
side-effects are elaborated in left-to-right order.

Benefits:

Improved portability of Common Lisp programs.

Conversion Cost:

This change is incompatible because it changes the result of some forms
that are not erroneous.  However, it's unlikely that very many users are
intentionally depending on the current behavior.  In addition, the
current behavior is not consistent across implementations, which makes
changing it less problematic.

Esthetics:

See "cost of non-adoption".

Discussion:

I wish CLtL did a much better job of explaining the philosophy of SETF,
and included some better examples of precisely what is meant by the
"`obvious' semantics" mentioned on page 99.  I will accept some of the
blame for this lack in the documentation. --Moon

This proposal is consistent with PUSH-EVALUATION-ORDER:ITEM in affirming
the left-right order of evaluation of subforms of generalized variable
access forms. 

It was pointed out that it is possible to get the required result for
the test case by modifying the get-setf-method for GETF (and other
setf-able items) to set up the bindings when the modified form is a
symbol, as is done in Spice Lisp.

The discussion seemed to be moving toward considering an alternative,
which is to require the setf method for GETF and LDB to bind temporary
values, e.g., 

"When it comes to efficiency, I'd rather have a little extra variable
shuffling in Getf and places like that than in all setf's involving a
symbol destination."

(define-setf-method getf (place prop &optional default &environment env)
  (multiple-value-bind (temps values stores set get)
		       (get-setf-method place env)
    (let ((newval (gensym))
	  (ptemp (gensym))
	  (def-temp (gensym)))
      (values `(,@temps ,(car stores) ,ptemp ,@(if default
`(,def-temp)))
	      `(,@values ,get ,prop ,@(if default `(,default)))
	      `(,newval)
	      `(progn (setq ,(car stores)
			    (%primitive putf ,(car stores) ,ptemp ,newval))
		      ,set
		      ,newval)
	      `(getf ,(car stores) ,ptemp ,@(if default `(,def-temp)))))))