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

Unhygienic macros



Hello again.

This is more of an exploratory rant than a question. Bear with me.

I'm still struggling to learn the new syntax system, though I'm getting
even more convinced of its power. For example, I only recently realised
that you can export a macro from a module without exporting the function
that its expansion calls, something that's impossible with
defmacro-style systems (unless you insert a quoted function object into
the expansion, which is questionable practice at best).

Still, the concepts behind the syntax system are hard to grasp. I was
trying to implement an unhygienic macro without using define-macro, such
that inside the body of a macro application, a special variable is bound
to a value. A contrived example would be a special "rec" form, that
would be used like this:

(define fact (rec (x) (if (zero? x) 1 (* x (self (- x 1))))))

The problem here is how to make the "self" identifier bound to a
variable that is only declared during the macro expansion. Using
hygienic macros, the variables cannot be shadowed during expansion, so
"self" remains bound at top level, if at all.

All right, you can traverse through the expression manually and then do
the rebindings even with syntax-rules
<news:87oflzcdwt.fsf@radish.petrofsky.org>, but that is not very
practical. :)

I had a look at class.ss to see how it handles the implicit binding of
"self". Following the example, I ended up with this implementation of rec:

(define-syntax (rec stx)
 (syntax-case stx () 
  ((_ args body ...)
   (with-syntax ((self (datum->syntax-object stx 'self)))
     #'(letrec ((self (lambda args body ...)))
         self)))))

All right, it works. Whee. So my question is not how to do this, but
rather, what the hell am I doing? I would like to understand the
gimmicks I end up using.

Seeing how with-syntax is defined, this expands to:

(define-syntax (rec stx)
 (syntax-case stx () 
  ((_ args body ...)
   (syntax-case (datum->syntax-object stx 'self) ()
   (self #'(letrec ((self (lambda args body ...)))
              self))))))

So how does this work? Does the datum->syntax-object function return the
identifier "self" that is _free_ inside the expression stx? I have until
now thought that the "context information" of a syntax object refers to
the variables that are _visible_ (ie. bound) where the expression
represented by the syntax first appeared.

I'm afraid I'm right now unable to formulate any specific question. I'd
just like some clarification on how the syntax system works, what
information exactly does a syntax object contain, at what stage are
identifiers resolved and variables bound, etc... The syntax chapter of
the manual doesn't really explain these thoroughly.

One practical question, though: is there a way to "unquote" inside a
(syntax ...) form, so one could more easily embed a computed syntax
object inside a template? Of course one can always pre-bind them to
template variables with with-syntax, but it's not always as compact.

Enough ranting for now. All tidbits of information are gratefully
accepted.


Lauri Alanko
la@iki.fi