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

Re: need help with syntax-case/syntax/datum->syntax-object



Quoting Doug Orleans:
> My (shaky) understanding of syntax-case makes me believe this should
> instead be:
> 
> 	 (lambda (stx)
> 	   (syntax-case stx (literal-identifier ···)
> 	     ((generated-identifier . pattern) (syntax template))
> 	     ···))

Yep - thanks.

> Okay, here's what I'm trying to do [...]

Condensed version: 

 * The macro `lambda-foo' introduces a binding that is supposed to
   capture variables introduced at the use-site of `lambda-foo' (i.e.,
   it's intentially breaking hygiene).

 * The macro `traced-lambda-foo-1' expands to a use of `lambda-foo'.

 * The binding introduced by `lambda-foo' via `traced-lambda-foo-1'
   doesn't capture variables introduced at the use-site of
   `traced-lambda-foo-1'.

Here's the reason: `lambda-foo' does capture variables at the use-site
of `lambda-foo'. But, in the case of `traced-lambda-foo-1', the
use-site is in the implementation of the `traced-lambda-foo-1' macro,
*not* the original use-site of `traced-lambda-foo-1'.

So, it captures the `foo' in `(display foo)' here:

>   (define-syntax traced-lambda-foo-1
>     (lambda (x)
>       (syntax-case x ()
> 	((_ body ...)
> 	 (syntax (lambda-foo (display foo) (newline) body ...))))))

but not the `foo' in `(+ foo 1)' here:

>   > (define add1 (traced-lambda-foo-1 (+ foo 1)))


> Is there a way to get this to work right?

Not a good one that I've found.

Since `lambda-foo' is trying to discover the original context through
the leading keyword at the use-site, you could do something like this:

  (define-syntax traced-lambda-foo-3
    (lambda (x)
      (syntax-case x ()
	((_ body ...)
	 (with-syntax ((lambda-foo (datum->syntax-object (syntax _) 
                                                         'lambda-foo))
                       (foo (datum->syntax-object (syntax _) 'foo)))
	   (syntax (lambda-foo (display foo) (newline) body ...)))))))

but `traced-lambda-foo-3' will only work in a context where
`lambda-foo' is also bound. That may be good enough for your puposes,
but it's not a nice solution in general.

In general, the only way I know to avoid the problem is to avoid macros
that expand into uses of hygiene-breaking macros. For example,
`class-100' expands to `class*/names' (hygienic) instead of `class'
(which introduces non-hygienic `this', `super-instantiate', and
`super-make-object' bindings).

> Why does it seem to do the right thing when expanded incrementally,
> but not when it's expanded all at once?

Unfortunately, `expand' is not the fixpoint of `expand-once'. (I see
that the documentation neglects to mention that important fact. Oops!)

As far as I can tell, the underlying expansion mechanism isn't actually
compatible with `expand-once'. To make it almost work --- usually
enough for debugging --- MzScheme's `expand-once' loses a bit of "mark"
information, which traces the introduction of variables by macros. It's
exactly that loss that makes `traced-lambda-foo-1' work piecewise.

[BTW, multiple calls to `expand' do work reliably, without flattening
the syntax to an S-expression, thanks to the way that "mark"
information is lost.]

Matthew