CLHS: Issue READ-MODIFY-WRITE-EVALUATION-ORDER Writeup

#

## Issue READ-MODIFY-WRITE-EVALUATION-ORDER Writeup

    Issue:            READ-MODIFY-WRITE-EVALUATION-ORDER
    References:       DEFINE-MODIFY-MACRO
                      DECF, INCF, POP, PUSH, PUSHNEW, REMF
    Related issues:   Issue SETF-SUB-METHODS
                      Issue PUSH-EVALUATION-ORDER
    Category:         Clarification, change
    Edit history:     v1, 17 Dec 1990, Sandra Loosemore
                      v2, 19 Feb 1991, Sandra Loosemore

    Problem description:

    Proposal SETF-SUB-METHODS:DELAYED-ACCESS-STORES states that "reading
    the value" of a generalized variable reference is not part of the
    series of evaluations that must be done in left-to-right order.  That
    issue only addressed the problem in the context of SETF places such
    as GETF, LDB, and MASK-FIELD whose SETF methods include an implicit
    read-modify-write operation on a nested place form.

    There is a similar order-of-evaluation issue for explicit
    read-modify-write macros such as INCF and those defined with
    DEFINE-MODIFY-MACRO: if the access to the place is performed before
    other arguments to the macro are evaluated, any side-effects of those
    evaluations on the value of the place are lost.

    In the case of SETF of GETF and friends, issue SETF-SUB-METHODS placed
    constraints on the order of evaluation of subforms within the values
    returned by GET-SETF-METHOD.  In the case of INCF and friends, the
    question is the order of evaluation in the expansion of the
    read-modify-write macro.

    Proposal READ-MODIFY-WRITE-EVALUATION-ORDER:DELAYED-ACCESS-STORES:

    Clarify that the exception of "reading the value" of a generalized
    variable reference from the series of evaluations that must be done in
    left-to-right order applies to the read-modify-write macros DECF,
    INCF, POP, PUSH, PUSHNEW, REMF, SHIFTF and all macros defined with
    DEFINE-MODIFY-MACRO.

    Specifically:

      For DECF, INCF, POP, REMF, and all macros defined with
      DEFINE-MODIFY-MACRO:

      These macros are of the form
        (<operator> <place> . <other-arguments>)
      The order of evaluation should be:
        - all subforms of the <place>, in the order specified by the second
          value of GET-SETF-METHOD for that <place>
        - the <other-arguments>, in left-to-right order
        - the access to <place>
        - the computation of the new value to be stored
        - the store into <place>

      For PUSH and PUSHNEW:

      These macros are of the form
        (<operator> <object> <place> . <other-arguments>)
      The order of evaluation should be:
        - the <object>
        - all subforms of the <place>, in the order specified by the second
          value of GET-SETF-METHOD for that <place>
        - the <other-arguments>, in left-to-right order
        - the access to <place>
        - the computation of the new value to be stored
        - the store into <place>

    In other words, the subforms of the <place> argument and any other
    argument subforms should be evaluated in left-to-right order, but the
    actual access of the <place> happens after all of the subform evaluations
    and just before the computation and store of the new value.

    Examples:

    These two examples parallel examples 6 and 7 (respectively) from
    issue SETF-SUB-METHODS.

    1. (setq r (list 'a 1 'b 2 'c 3))
       (setf (getf r 'b) (progn (setq r nil) 6))
       r => (b 6)

       (setq r 5)
       (incf r (progn (setq r 0) 1))
       r => 1

       In both cases, the place form that is the target of the read-modify-write
       operation is the variable R.  The access of this place is delayed until
       the other subforms have been evaluated, so the value of R that is
       read as part of the read-modify-write operation reflects the SETQ.

    2. (setq s (setq r (list (list 'a 1 'b 2 'c 3))))
       (setf (getf (car r) 'b)
             (progn (setq r nil) 6))
       r => nil
       s => ((A 1 B 6 C 3))

       (setq s (setq r (list 5)))
       (incf (car r) (progn (setq r (list 0)) 1))
       r => (0)
       s => (6)

       In both cases, the nested place form is the value of (CAR R).  Note that
       the SETQ does not affect the read-modify-write operation because the
       value of R has already been saved in a temporary variable as part of
       evaluating the subforms of the nested place form.  The read-modify-write
       operation applies to the CAR of this value.

    Rationale:

    This makes the rules for conceptual "read-modify-write" operations
    consistent, regardless of whether or not the operation is specified by
    a special-case SETF place (such as GETF) or a macro such as those
    defined by DEFINE-MODIFY-MACRO.

    Current Practice:

    Neither Lucid CL nor Allegro CL implement this proposal.  In both
    implementations, the access to the place subform apparently happens in
    its normal left-to-right order.

    Cost to Implementors:

    Small.

    Cost to Users:

    Hard to say.  Some programs that depend on the strict left-to-right
    order of evaluation now supported in some implementations may break,
    but probably such programs are no more common than those that were
    broken by the adoption of proposal SETF-SUB-METHODS:DELAYED-ACCESS-STORES.

    Cost of non-adoption:

    Read-modify-write macros such as INCF and macros defined with
    DEFINE-MODIFY-MACRO continue to have the same problems with losing
    multiple updates to the place that were solved for SETF of GETF by
    adoption of SETF-SUB-METHODS:DELAYED-ACCESS-STORES.

    Performance impact:

    Probably not applicable.

    Benefits:

    The cost of non-adoption is avoided.

    Esthetics:

    Specifying the same order of evaluation rules for all conceptual
    read-modify-write operations is better than arbitrarily having a
    difference between those defined as special-case SETF places (like GETF)
    and those defined as macros.

    Discussion:

    This proposal does not change the order of evaluation for the macros
    ROTATEF, SHIFTF, ASSERT, CHECK-TYPE, CTYPECASE, and CCASE.  These
    macros all have their own peculiar order-of-evaluation rules already.

Copyright 1996-2005, LispWorks Ltd. All rights reserved.