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

``Update functions'' in Scheme.



    Date: Mon, 9 May 88 22:51:49 edt
    From: lyn@BASEL.AI.MIT.EDU (Franklyn Turbak)


    > I don't know about Common Lisp's SETF, but in the T dialect of Scheme there is a
    > difference between the two.  When you write (SET (FOO ...) ...), the operation
    > FOO can specify how it is to be set, i.e., it can handle the operation (SETTER
    > FOO) and return a setter procedure, which then does the setting appropriately.

Pages 94-107 of Common Lisp The Language will probably clarify Common Lisp's
SETF semantics.

    All SETF does is to provide a connection between an accessor and its
    corresponding mutator.  I suggested that an appropriate naming convention
    served many of the same purposes as SETF.

SETF is a macro.  It does more complicated source-to-source transformations
than simple name substitution.

  (LDB (BYTE 4 2) (AREF MY-ARRAY 0))
      returns four bits from MY-ARRAY's first element (a number). 
  (SETF (LDB (BYTE 4 2)) (AREF MY-ARRAY 0))
      sets the four bits in MY-ARRAY's first element (a number).

This is a case which can't be modeled as simply turning LDB into a call to
SET-LDB.  (AREF MY-ARRAY 0) may fetch a number from MY-ARRAY and pass it to
SET-LDB, but that won't change the number that's stored in MY-ARRAY.

For the interested, here's a brief description of SETF.

(SETF place value)

`place' is something that looks syntactically like a function call.

You define a SETF method for a given place.  The SETF method tells how to
access and modify that place.

For example:  
 
        (SETF (CAR xxx) value)   The "place" is (CAR xxx)

The SETF method for CAR says that to change the value in a CAR place, expand
into (RPLACA xxx value).  To access the value in the place, expand into 
(CAR xxx).

Macros other than SETF use this information.  (INCF place) increments the value
stored in a place.  For INCF, `place' has to be read as well as written.

(INCF MY-VARIABLE) 
has roughly the same effect as (SETF MY-VARIABLE (+ 1 MY-VARIABLE)). 

But SETF methods give a little more precision than that.  If the expansion was
just (INCF xxx) => (SETF xxx (+ 1 xxx)), consider:

       (INCF (AREF *MY-ARRAY* (INCF *SUBSCRIPT*))) 

expanding into:

       (SETF (AREF *MY-ARRAY* (INCF *SUBSCRIPT*))
             (+ 1 (AREF *MY-ARRAY* (INCF *SUBSCRIPT*))))

We'd only like (INCF *SUBSCRIPT*) evaluated once.  It only occurs once in the
source code.  But the straightforward expansion includes it twice.

SETF methods take care of making sure that \subforms of `place'/ are only
evaluated once.

Written using SETF methods, INCF actually expands into:
       (LET ((SUBSCRIPT (INCF *SUBSCRIPT*))
             (ARRAY     *MY-ARRAY*))
         (ARRAY-STORE ARRAY SUBSCRIPT (1+ (AREF ARRAY SUBSCRIPT))))

--------------------

So SETF is NOT a function, and actually provides a set of source code
transformations that can be used to disassemble references to `place's and
reassemble them.

In the simplest case, SETF can be thought of as a simple link between an
accessor and a mutator.  But SETF's underlying mechanism, SETF methods, give
far more power.

    unfortunately, as Bard Bloom has pointed out to me, a simple naming convention
    does not work if we wish to use the relationship between accessor and mutator in
    a more abstract way.  An example derived from the one Bard showed me is:

      (define (act-foolishly-on accessor object)
        (setf (accessor object) (list (accessor object) "silly"))
        object)

Common LISP's SETF won't work in this example.  SETF does a syntactic
transformation on its first operand.  It doesn't evaluate the first operand.

Compiling ACT-FOOLISHLY-ON will give the error that no SETF method is defined
for "accessor."

Notice: these are only the Common Lisp semantics for SETF.  It's entirely
possible that some functional (vs. macrological) definition of SETF can be
reached.  But my hunch is that it \will/ require some special evaluation rules.

-- Stephen