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

``Update functions'' in Scheme.

> 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.
> This is not a matter of simple renaming; rather, it is a very useful programming
> construct which lets you define new operations and their setters within the
> existing syntax.

The purpose of my message suggesting simple renaming vs. SETF [e.g.  using
(set!vector-ref v 10 'a) rather than (setf (vector-ref v 10) 'a), where
SET!VECTOR-REF is just another name for VECTOR-SET!] was to emphasize that
there is nothing particularly magical about SETF.  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.

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"))

That is, 

  (act-foolishly-on car '(1 2 3))   should return   ((1 "silly") 2 3)


  (act-foolishly-on car '(1 2 3))   should return   (1 (2 3) "silly")

ACT-FOOLISHLY-ON can "make sense" for other accessor/mutator pairs as well
(though not STRING-REF / STRING-SET!)

But since procedures are first-class in Scheme, there is an easy way to get this
more abstract behavior using a table that maps accessors to mutators.  Suppose
we have table abstraction based on the procedures MAKE-TABLE, TABLE-INSERT!,
and TABLE-LOOKUP.  Then we can obtain the more abstract behavior as follows:

; --------------------------------------------------------------------------

; Make a 1-dimensional table 
(define *setter-table* (make-table))

; Insert setting procedures into the table keyed by the accessing procedures
;                            |  ACCESSOR  |   MUTATOR   |
(table-insert! *setter-table* car          set-car!      )
(table-insert! *setter-table* cdr          set-cdr!      )
(table-insert! *setter-table* vector-ref   vector-set!   )
(table-insert! *setter-table* string-ref   string-set!   )
(table-insert! *setter-table* table-lookup table-insert! ) ; !
; . . . and so on.

; The procedure SETTER gets the mutator from the accessor
(define (setter accessor)
  (table-lookup *setter-table* accessor))

; Then we can use SETTER for the simple examples . . .

((setter car) p 5)

((setter vector-ref) v 10 'a)

; . . . as well as Bard's more complex example:

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

; --------------------------------------------------------------------------

The key point here is that Scheme already has enough power to express
the abstract relationship between accessors and mutators; there is no
need to "extend" the language to provide this feature.

- Lyn -