[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"))
object)
That is,
(act-foolishly-on car '(1 2 3)) should return ((1 "silly") 2 3)
and
(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"))
object)
; --------------------------------------------------------------------------
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 -