chicken-users
[Top][All Lists]
Advanced

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

Re: [Chicken-users] Re: Explicit Renaming Macros Help/Tutorial?


From: Juergen Lorenz
Subject: Re: [Chicken-users] Re: Explicit Renaming Macros Help/Tutorial?
Date: Tue, 9 Jun 2009 17:16:48 +0200

Here is a mini tutorial of explicit renaming for all who are interested
in it:


Mini-tutorial on explicit renaming macros in Chicken
----------------------------------------------------

Author: Juergen Lorenz
        ju (at) jugilo (dot) de
Date:   Jun 9th, 2009

The following is an attempt to explain, how to write hygienic macros in
Chicken with explicit renaming. It supposes, that the reader knows, how
to write other low-level-macros like define-macro in Chicken-3 and other
Schemes. As you will see, explicit renaming macros are even easier to
write than those with define-macro. This is because there is a brute
force method to avoid variable capture. 

Let's consider a trivial macro a la Chicken-3 without any
considerations on hygiene and variable capture:
 
  (define-macro swap!  ; wrong
    (lambda (x y)
      `(let ((tmp ,x))
         (set! ,x ,y)
         (set! ,y tmp))))
     
You might be surprised, but the replacement text, that within the
backquotes, need not be changed in an explicit renaming macro, at least
in a first step. What has to be changed, is the signature: explicit
renaming transformers are always procedures of three variables, usually
called form, rename, compare:

  (define-syntax swap!
    (lambda (form rename compare)
      ...
      `(let ((tmp ,x))
         (set! ,x ,y)
         (set! ,y tmp))))
     
and you have to destructure the form argument, either by hand, or by
means of a macro from the matchable package, to fill in the ellipsis
above. That is, without any helper macros, you get

  (define-syntax swap!
    (lambda (form rename compare)
      (let (
        (x (cadr form))
        (y (caddr form))
        )
        `(let ((tmp ,x))
            (set! ,x ,y)
            (set! ,y tmp)))))

In this form, the new macro is the same as our first attempt with
define-macro. We haven't bothered about variable capture or hygiene at
all. But this macro already works. You can test it, say with

  (let ((x 'x) (y 'y)) (swap! x y) (list x y))

and see if it does what it is supposed to do. Of course, it is not
correct, since if a client uses tmp as one of his or her arguments, the
macro will crash.

In the classical macro systems, defmacro in Common Lisp or define-macro
in some Schemes, you now have to think carefully, which macro variables
are in danger of variable capture and use an uninterned symbol for them,
tmp in the current example. Worse than that, contrary to Common Lisp,
Scheme allows to use any name for variables, even keyword names as let,
set!, ... so that define-macro can never create a hygienic swap! macro.
Here is, where renaming comes into the play. You don't need gensym, use
rename instead.  And you needn't bother, which symbol to rename,
rename all.

What does that mean, "all"? It's easy in the present example, everything
within the backquote, except those symbols, which are already unquoted.
But in more complicated examples, you should use expand, to see the
replacement text of your macro calls.

  (pp (expand '(swap! x y)))

will result in the replacement text

  (let ((tmp x)) (set! x y) (set! y tmp))

Only x and y are arguments of your swap! call, hence everything else in
the replacement text should be renamed: namely let, tmp and set!. So a
hygienic version of swap! would be

  (define-syntax swap!
    (lambda (form rename compare)
      (let (
        (x (cadr form))
        (y (caddr form))
        )
        `(,(rename 'let) ((,(rename 'tmp) ,x))
           (,(rename 'set!) ,x ,y)
           (,(rename 'set!) ,y ,(rename 'tmp))))))

If you repeat the expand call above, you will get the same replacement
text, but with numbers suffixed to the renamed symbols, something like

  (let11 ((tmp12 x)) (set!13 x y) (set!13 y tmp12))

These renamed symbols have the same meaning as the original names without
suffixes, but serve as aliases which the client cannot use under any
circumstances.

Note, that the two appearances of tmp and set! are renamed to the same
alias, the rename operator is referentially transparent. Note also, that
these renamed symbols are much easier to interpret than gensym'ed ones!

I personally do not like these rename calls within the backquoted
expression, because you loose the visual similarity between the
backquoted expression and the resulting replacement text. Therefore I
prefer this version, which is equivalent

  (define-syntax swap!
    (lambda (form rename compare)
      (let (
        (x (cadr form))
        (y (caddr form))
        (%tmp (rename 'tmp))
        (%let (rename 'let))
        (%set! (rename 'set!))
        )
        `(,%let ((,%tmp ,x))
           (,%set! ,x ,y)
           (,%set! ,y ,%tmp)))))

You can think of the two characters ,% as an identity operator.

You see, explicit renaming macros are a bit more verbose than classical
ones with define-macro. But everybody who is able to write the latter,
is able to write the former as well. It's even easier!

Hope, this helps.


Dr. Juergen Lorenz
Flensburger Str. 12
D-10557 Berlin




reply via email to

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