chicken-users
[Top][All Lists]
Advanced

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

Re: [Chicken-users] need help with hygienic macros


From: Jörg F . Wittenberger
Subject: Re: [Chicken-users] need help with hygienic macros
Date: 16 May 2013 21:19:01 +0200

Hi all,

the past days I wasted trying to wrap my head around a hygienic
replacement (using syntax-rules) for a macro, which is all too easily
done in an unhygienic way (with all the downsides of accidental
variable captures).

I need some syntax to define a set of procedures, which all have the
same list of formal arguments.  However those arguments are about to
change and therefore MUST NOT appear at the definition side.  Access
to those formals shall be available within the procedure body by means
of some special syntax.

Like this: (`deftig` would be the syntax to define the procedure.)
I want to write:

(deftig f1 (+ (get-x) (get-y)))

and get:

(f1 1 2) => 3

In `define-macro` this would be:

(define-macro (deftig name . body) `(define (,name x y) . ,body))
(define-macro (get-x) x)
(define-macro (get-y) y)

  However:
   (deftig f2 (let ((x 7)) (+ (get-x) (get-y))))
   (f2 1 2) => 9  -- not what I want

I've got some *partial* success with this:

;; breed is a helper to access thos hidden formals:

(define-syntax breed
 (syntax-rules ___ ()
   ((_ (x y) body_ ___)
    (letrec-syntax
         ((h (syntax-rules (get-x get-y)
               ((_ (get-x)) x)
               ((_ (get-y)) y)
               )))
      (h body_) ___))))

(define-syntax deftig
 (syntax-rules ()
   ((_ name body ...)
    (define name
      (lambda (x1 y1)
         (breed (x1 y1) body ...))))
   ))

With this there is

(deftig f3 (get-x))

(f3 1 2) => 1

And - as hoped for `x` is unbound in

(deftig f3a x)

The downside:

(deftig f4 (+ (get-x) (get-y)))

Gives an error at macro expansion time: no matching rule.  Easy to solve:

(define-syntax breed
 (syntax-rules ___ ()
   ((_ (x y) body_ ___)
    (letrec-syntax
         ((h (syntax-rules (get-x get-y)
               ((_ (get-x)) x)
               ((_ (get-y)) y)
               ((_ (f)) ((h f)))
               ((_ (f a ...)) ((h f) (h a) ...))
               ((_ a) a)
               )))
      (h body_) ___))))

(f4 1 2) => 3

BUT: I feel I'm doing it all wrong!!!

Any syntax to be used in the procedure body needs to be defined in
`breed` -- an that's NOT what I want.  `Breed` quickly becomes a mess.

Question: Am I missing the right recipe?  (Googling the wrong keywords?)
Or am I doing something fundamentally wrong, as I'm suspecting?

I can't believe that there is no hygienic way to get this done.

I'm ready to live with some complexity in the solution.  But the best
I've been able to come up with so far is along the lines of passing
identifiers for `get-x` and friends like this:

(define-syntax let-alias
  (syntax-rules ()
    ((_ ((id alias) ...) body ...)
     (let-syntax ((helper (syntax-rules ()
                            ((_ id ...) (begin body ...)))))
       (helper alias ...)))))

(define-syntax defsoup
 (syntax-rules ()
   ((_ (name x1 call) body ...)
    (define name
      (lambda (x y)
         (let-alias ((x1 y)
                     (call (x y)))
            body ...))))
   ))

(defsoup (f5 gt-y cook) (+ 1 cook))

(f5 (lambda (v) (* 2 v)) 3) => 7

This is a "partial" success, because it's hygienic AND does NOT force
me to rewrite all syntax in `let-alias` (the latter reused as stolen).

But it's still a partial failure, because I still have to write down
the same list of arguments with each and every `defsoup` and in
`breed` above.

How the hell would I mix those approachs?  I want those `gt-y` and
`cook` to be magic (e.g. syntax keywords or whatever) and NOT as
formals in the definition side.

(Well, I know one solution already: have a `define-macro` as at the
begin wrapped around the `defsoup`.  But isn't that plain ugly?)

Thankful for any hint to enlighten me.

/Jörg


......




reply via email to

[Prev in Thread] Current Thread [Next in Thread]