Scheme Setf

From Tue Jul 07 16:18:18 1998

Path: News.CoLi.Uni-SB.DE!!!!!!!!!!!!not-for-mail

From: Newsgroups: comp.lang.scheme Subject: setf -- a polymorphic, generic setter -- as a simple Scheme macro Date: Tue, 07 Jul 1998 16:18:18 GMT Organization: Deja News - The Leader in Internet Discussion Message-ID: <6nthoa$bvr$> Reply-To: Summary: LISP's setf as a Scheme macro Keywords: polymorphism, setter, overloading, generic function, Scheme, LISP, macro X-Article-Creation-Date: Tue Jul 07 16:18:18 1998 GMT X-Http-User-Agent: Mozilla/4.05 (Macintosh; I; PPC, Nav) Xref: News.CoLi.Uni-SB.DE comp.lang.scheme:22273 Xcanpos: shelf.ccd0/199807172201!0005604418

This is to discuss how a LISP-like setf form can be used and implemented in Scheme. First, allow me to show a few examples of setf! usage:

> (define al '((1 "one") (2 "two")))
> (assv 1 al)
(1 "one")
> (setf! (cadr (assv 1 al)) "-one-")
> (assv 1 al)
(1 "-one-")
> (setf! (car (assv 1 al)) 3)
> (assv 1 al)
> al
((3 "-one-") (2 "two"))

> (define s (string-append "abcd" ""))
> (string-ref s 1)
> (setf! (string-ref s 1) #\B)
> s

> (define v (vector 1 2 '() ""))
> (vector-ref v 2)
> (setf! (vector-ref v 2) (list (vector-ref v 3) 3))
> v
#(1 2 ("" 3) "")

        ; a more elaborate, and admittedly, contrived, example
> (define tree #f)
> (setf! tree '((a . b) . (c . (e . f))))

(define (tree-ref tree . dirs)
  (if (null? dirs) tree
    (apply tree-ref (cons (if (car dirs) (cdr tree) (car tree)) (cdr dirs)))))

(define (tree-set! tree dirval1 dirval2 . dirs)
  (if (null? dirs) ((if dirval1 set-cdr! set-car!) tree dirval2)
    (apply tree-set! (cons (if dirval1 (cdr tree) (car tree))
                           (cons dirval2 dirs)))))

> (tree-ref tree #f #f)
> (setf! (tree-ref tree #f #f) 'x)
> (tree-ref tree #f #f)
> (tree-ref tree #f)
(x . b)
        ; the following prunes the tree
> (setf! (tree-ref tree #f) 'leaf)
> (tree-ref tree #f)
        ; and the following grows it back
> (setf! (tree-ref tree #f) '((z . u) . v))
> (tree-ref tree #f #f #t)

The setf! form is implemented as a Scheme macro. The following table
shows code re-writing it performs. The first column tells what one can
enter, while the second column shows what actually is being executed
by a Scheme system after macro expansion:

(setf! (car L) V)         ==>   (set-car! L V)
(setf! (cdr L) V)         ==>   (set-cdr! L V)
(setf! (car (cdr L)) V)   ==>   (set-car! (cdr L) V)
(setf! (cddr L)) V)       ==>   (set-cdr! (cdr L) V)

(setf! (string-ref s i) c)   ==>   (string-set! s i c)
(setf! (vector-ref v i) c)   ==>   (vector-set! v i c)

In general,
(setf! (ANYTHING-ref v ....) c)   ==>   (ANYTHING-set! v ... c)
where ANYTHING is, well, anything - any combination of allowed characters.

and (setf! X V)   ==>   (set! X V)

The setf! form is implemented as follows (Gambit-C 3.0):

; setf! - a polymorphic generic setter
(define-macro (setf! F V)
        ; symbol->string chopping off a trailing -ref if any
  (define (-ref-less sym)
    (let* ((str (symbol->string sym)) (suffix "-ref")
           (s-pos (- (string-length str) (string-length suffix))))
      (if (negative? s-pos) str
        (let loop ((i 0))
              ((>= i (string-length suffix)) (substring str 0 s-pos))
              ((char=? (string-ref suffix i) (string-ref str (+ i s-pos)))
               (loop (+ 1 i)))
              (else str))))))

  (if (not (pair? F)) `(set! ,F ,V)
    (case (car F)
          ((car) `(set-car! ,@(cdr F) ,V))
          ((cdr) `(set-cdr! ,@(cdr F) ,V))
          ((cadr) `(setf! (car (cdr ,@(cdr F))) ,V))
          ((cddr) `(setf! (cdr (cdr ,@(cdr F))) ,V))
                ; I need to handle other cadda..vers but I'm tired...
          (else `(,(string->symbol (string-append (-ref-less (car F)) "-set!"))
                  ,@(cdr F) ,V)))))

-----== Posted via Deja News, The Leader in Internet Discussion ==-----   Create Your Own Free
Member Forum