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

Re: Initial comments & questions

This page is part of the web mail archives of SRFI 53 from before July 7th, 2015. The new archives for SRFI 53 contain all messages, not just those from before July 7th, 2015.

At Tue, 23 Mar 2004 10:00:18 -0800 (PST), campbell@xxxxxxxxxxxxxxxxxxxxxxxxxxx wrote:
> > >   - Very little is mentioned about hygiene, which I'm worried about.
> > >   - Very little is mentioned about shadowing.
> > 
> > I'll see if I can come up with something intelligent to say about this.
> I expect the reason you're avoiding those mentions is that you're
> assuming the underlying SYNTAX-RULES implementation deals with them,
> but I think this is a dangerous assumption that could potentially cause
> _very_ unportable code.

But syntax-rules hygiene can already be broken, and computation-rules
just gives you more ways to do so.  What more can be said?

As an example, suppose we want to implement with-slots from CLOS:

  (with-slots (name1 name2 ...) obj

where name1 is bound to (slot-ref obj 'name1) in the body of the form.

For the sake of those not using a CLOS-like system we'll use the
following for our examples:

  (define (slot-ref obj slot)
    (vector-ref obj (case slot ((a) 0) ((b) 1) ((c 2)) ((d 3)))))

  (define my-obj #(1 10 100 1000))

First we want a syntax-replace macro:

(define-syntax-computation syntax-equal?
  (computation-rules ()
    ((syntax-equal? (h1 . t1) (h2 . t2))
     (syntax-if (syntax-equal? h1 h2)
       (syntax-equal? t1 t2)
       (syntax-return #f)))
    ((syntax-equal? #(h1 t1 ...) #(h2 t2 ...))
     (syntax-if (syntax-equal? h1 h2)
       (syntax-equal? (t1 ...) (t2 ...))
       (syntax-return #f)))
    ((syntax-equal? x y)
     (syntax-eq? x y))))

(define-syntax-computation syntax-list->vector
  (computation-rules ()
    ((syntax-list->vector ls)
     (syntax-do (rev <- (syntax-reverse ls))
       (syntax-list->vector rev #())))
    ((syntax-list->vector () #(v1 ...))
     (syntax-return #(v1 ...)))
    ((syntax-list->vector (h . t) #(v1 ...))
     (syntax-list->vector t #(h v1 ...)))

(define-syntax-computation syntax-replace
  (computation-rules ()
    ((syntax-replace from to (h . t))
     (syntax-if (syntax-equal? (h . t) from)
       (syntax-return to)
       (syntax-do (h1 <- (syntax-replace from to h))
                  (t1 <- (syntax-replace from to t))
         (syntax-return (h1 . t1)))))
    ((syntax-replace from to #(h t ...))
     (syntax-if (syntax-equal? #(h t ...) from)
       (syntax-return to)
       (syntax-do (h1 <- (syntax-replace from to h))
                  (t1 <- (syntax-replace from to (t ...)))
         (syntax-return (syntax-list->vector (h1 . t1))))))
    ((syntax-replace from to s)
     (syntax-if (syntax-equal? s from) (syntax-return to) (syntax-return s)))

Then the implementation is straightforward:

(define-syntax-computation with-slots-computation
  (computation-rules ()
    ((with-slots-computation () obj . body)
     (syntax-return (begin . body)))
    ((with-slots-computation (slot1 slot2 ...) obj . body)
     (syntax-do (inner1 <- (with-slots-computation (slot2 ...) obj . body))
                (inner2 <- (syntax-replace slot1 tmp inner1))
       (syntax-return (let ((tmp (slot-ref obj 'slot1))) inner2))))))

(define-syntax with-slots
  (syntax-rules ()
    ((with-slots slots obj . body)
     (syntax-run (with-slots-computation slots obj . body)))))

And our example works as expected:

(syntax-inspect (with-slots-computation (a b c) my-obj (+ a b c)))
=> (let ((tmp (slot-ref my-obj 'a)))
     (let ((tmp (slot-ref my-obj 'b)))
       (let ((tmp (slot-ref my-obj 'c)))
          (begin (+ tmp tmp tmp)))))

(with-slots (a b c) my-obj (+ a b c))
=> 111

The nested tmp bindings don't interfere with each other as expected.  If
we bind tmp in the body we're safe as well:

(with-slots (a b c) my-obj (let ((tmp 5)) (+ a b c)))
=> 111

Macros inside the body may safely expand into forms using tmp or any of
the replaced symbols:

(define-syntax add
  (syntax-rules ()
    ((add) 0)
    ((add x) x)
    ((add x y z ...) (let ((a (+ x y))) (add a z ...)))))

(with-slots (a b c) my-obj (let ((tmp 5)) (add a b c)))
=> 111

Also rebinding the slot names in the body works as you would expect:

(with-slots (a b c) my-obj (let ((a 5)) (add a b c)))
=> 115

The bad news?  The above computations take about 10 _minutes_ each on
Petite Chez.  I would not want to try this on MzScheme if it was "very
slow" in comparison to Chez!