This is the mail archive of the guile@cygnus.com mailing list for the guile project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: generic method names for collections



chris.bitmead@misys.com.au writes:
> 
> >> > (set! (ref vector 0) "foo")
> >>
> >> better, but still ugly.
> >>
> >
> >Why? It's precisely parallel to "a = 7;" vs. "b[10] = 99;" vs "x.y =
> >77" in C. Assignment to locations is made generic. This is a big win
> >because you only have to remember the names of accessors, not the
> >names of mutators.
> 
> I think it's quite pretty to look it, but I would have thought that it
> places
> unnecessary extra restrictions on the implementation.
> 

Nope.

> For example, if the implementation represented Scheme objects as C
> structures
> and passed around pointers to these objects within the Scheme run-time.
> (typedef struct SchemeObject * SCM; ). This works fine in standard Scheme,
> but for the ref implementation above, (ref ..) would have to return a
> pointer
> to a pointer to a structure in order to modify it. Which wouldn't work in
> this implementation.
> 
> Or am I missing something?
> 

You are missing something :-)

Namely, that `set!' is a syntactic form which doesn't actually have to
evaluate its first argument. I implemented a `setf!' macro for Guile
once which works like the above, and there was no need to make any
Guile procedures return references. Here's a brief sketch of how its
done:

(defmacro setf! (place value)
  (if (pair? place)
      `((setter ,(car place)) ,@(cdr place) ,value)
      `(set! place value)))

(define (setter proc)
  (procedure-property proc 'setter))

(set-procedure-property! 
 setter 'setter 
 (lambda (proc setter)
   (set-procedure-property! proc 'setter setter)))


(setf! (setter vector-ref) vector-set!)
(setf! (setter car) set-car!)
(setf! (setter cdr) set-cdr!)

;;; etc - add setters for other getters


Now you can do 

(define v '#(1 2 3))
(setf! (vector-ref v 0) 99)

And it will work. In fact, unlike with Common Lisp `setf', the
following will work right as well:

(define v '#(1 2 3))
(define foobar vector-ref)
(setf! (foobar v 0) 99)

Even something like

(define v '(#(1 2 3) #(4 5 6)))
(setf! (vector-ref (cadr v) 1) 99)

Will cause v to have the expected final value of
(#(1 2 3) #(4 99 6)) 

with only the code above. Neat, eh?

The only particular reason to change the name of setf! to set! is to
make ordinary macros written to use set! handle generalized setting
properly. However, you can write really handly macros using setf!
itself, like these:

;; append a list destructively in place, even if it is the empty list:
(defmacro appendf! (place list)
  `(setf! ,place (append! ,place ,list)))

;; delete from a list destructively in place, even if the result is the
;; empty list:
(defmacro deletef! (place element)
  `(setf! ,place (delete! ,place ,element)))

A subtlety comes up here in that the subforms of place are evaluated
twice, which may be a problem if they are not side-effect free. It
shouldn't be too hard to come up with a way to conveniently write
read-modify-update macros that work right, though. On the other hand,
trying to make even the case of (swapf place1 place2) work right is
probably what makes common lisp's setf so complicated.

 - Maciej