Issue: SETF-GET-DEFAULTReferences: SETF of GET (CLtL-II p. 240)
SETF of GETHASH (CLtL-II p. 438)
Related issues: Issue PUSHNEW-STORE-REQUIRED
Category: CLARIFICATION
Edit history: V1, 12 Feb 1991, Sandra Loosemore
Problem description:
In the descriptions of SETF of GET, GETF, and GETHASH, it is stated
that the optional default argument is ignored by SETF. It is not
clear whether this means that the *subform* corresponding to the
default argument that is ignored, or only the *result* of evaluating
the subform. The behavior is distinguishable in situations where the
subform has observable side-effects.
Proposal (SETF-GET-DEFAULT:EVALUATED-BUT-IGNORED):
Clarify that if the optional "default" argument is supplied in a call
to GET, GETF, or GETHASH appearing as a SETF place, that subform
is evaluated according to the normal left-to-right evaluation rules for
subforms of a SETF place. However, its value is ignored.
Examples:
#1: (setf (get 'foo 'message (print "hello world"))
"goodbye, cruel world")
=> prints "hello world"
#2: (let ((x nil))
(setf (getf x 'message (print "hello world"))
"goodbye, cruel world"))
=> prints "hello world"
#3: (let ((x (make-hash-table :test #'eq)))
(setf (gethash 'message x (print "hello world"))
"goodbye, cruel world"))
=> prints "hello world"
Rationale:
Discarding the form corresponding to the default argument would be
inconsistent with the rule that subforms of a SETF place are
evaluated in left-to-right order.
Current Practice:
Lucid version 4.0 does tests 1 and 3 correctly but fails on test 2.
Allegro version 3.1 performs all three tests correctly.
Symbolics Genera reportedly fails at least test 2.
Cost to Implementors:
Minor.
Cost to Users:
None.
Cost of non-adoption:
Implementations will continue to differ for no good reason.
Performance impact:
None.
Benefits:
The language specification is made more precise and consistent.
Esthetics:
Making the language more consistent is a good thing.
Discussion:
Kent Pitman notes:
Btw, someone (I think it was Allan Wechsler at Symbolics), that it
should be permissible for:
(LET ((X '(A 1 B 2)))
X)
=> (A 1 B 2)
because what the SETF -really- says is to make sure that (GETF X 'C 3)
will later return 3, which that value of X does. As such, I don't
even think the code should be ignored. I would prefer this
interpretation if we could get consensus, and would appreciate your
listing it as an option in your writeup, with me favoring it. Two
pieces of rationale you might add to the writeup are that (a) this
permits an application to be more space-conservative and (b) this
permits an application to not gratuitously modify pages of data that
don't really need to be modified, and hence may sometimes result in
smaller dumps or smaller working sets in some implementations that
either permit dumping an incremental image containing only the
modified pages, or that have an initial data set of copy-on-modify
pages.
As a fall-back, I think your clarification would be an improvement
over the status quo. But I think it's not as elegant.
Sandra Loosemore says:
I think we'd need to spec this out very carefully. Should this
behavior be required or simply permitted? Does an explicit default
of NIL do the same thing as no default? Does only SETF of GETF
behave this way, or should we generalize this to require/permit
*any* SETF place not to actually update the place unnecessarily?
In the general case, what predicate would be used to compare the
new value to the value that would be returned by the accessor?
While I agree this is an attractive idea on the surface, the more I
think about these questions, the more I become convinced that this
is an incompatible change and probably not a good idea overall.
-------