Re: Initial comments & questions Alex Shinn 31 Mar 2004 07:29 UTC

At Tue, 23 Mar 2004 10:00:18 -0800 (PST), 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!