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

Re: #%app problems with nested macro



Quoting "Greg Pettyjohn":
> But if I nest the second macro with the first --- like this:
> 
> (define-syntax my-let
>   (lambda (x)
> 
>     (define-syntax with-values
>       (lambda (x)
>         (syntax-case x ()
>           [(_ P C) #'(call-with-values (lambda () P) C)])))
> 
>     (syntax-case x ()
>       [(_ () body) #'((lambda () body))]
>       [(_ ((var* val*) ...) body)
>        #'((lambda (var* ...) body) val* ...)])))
> 
> I will get the following error:
> 
> compile: bad syntax; function application is not allowed, because no #%app
> syntax transformer is bound in: (lambda (x) (syntax-case x () ((_ p c)
> (syntax (call-with-values (lambda () p) c)))))

The right-hand side (RHS) of a `define-syntax' has a different
top-level environment than the `define-syntax' expression itself,
because the RHS is in a dififferent phase.

The initial namespace imports all of the MzScheme forms into the
run-time ("phase 0") top-level environment and into the compile-time
("phase 1") top-level environment. Thus, `cons' is available in both of
the following places:

  (define-syntax x ... (cons 1 2) ...)
  (cons 1 2)

But, in priciple, they are different `cons'es --- one for compilation
today on my machine, and another for execution next week on someone
else's machine.


If you put a `define-syntax' in the RHS of a `define-syntax', then the
RHS of the inner `define-syntax' lives at compiler-compile time ---
"phase 2". MzScheme doesn't provide a mechaism for importing into
phases deeper than 1, so the inner RHS has no initial bindings. (In
thoery, MzScheme could support phase-N imports. We'll look at this
possibility in v201.)


The module system lets you shift phases through `require-for-syntax'.
But such a shift is needed only when you're defining syntax used in
phase 1 to implement the transformation of phase 0 code.

In this case, my guess is that references to `with-values' will appear
in the expansion of `my-let' (or `my-let-values'?), as opposed to
appearing in code that generates the expansion of `my-let'. If my guess
is correct, then you'll have to implement `my-let' within a module, and
put `with-values' in the same module (unexported).


> I tried writing this macro at top level:
> 
> (define-syntax #%app
>       (lambda (x)
>         (syntax-case x ()
>           [(_ rator rands ...) #'(apply rator rands ...)])))
> 
> When I click execute, it goes forever (actually, I didn't wait that long).

In this case, there's a hidden `#%app' before apply', which is why it
loops.


> If I put the #%app macro at the same scope as with values:
> 
> (define-syntax my-let
>   (lambda (x)
> 
>     (define-syntax #%app
>       (lambda (x)
>         (syntax-case x ()
>           [(_ rator rands ...) #'(apply rator rands ...)])))
> 
>     (define-syntax with-values
>       (lambda (x)
>         (syntax-case x ()
>           [(_ P C) #'(call-with-values (lambda () P) C)])))
> 
>     (syntax-case x ()
>       [(_ () body) #'((lambda () body))]
>       [(_ ((var* val*) ...) body)
>        #'((lambda (var* ...) body) val* ...)])))
> 
> I get:
> 
> compile: identifier used out of context in: #%app

Absolutely confusing.

It turns out that nothing past the `#%app' definition matters. And if
we can simplify the `#%app' as follows, we still get the out-of-context
error:

 (define-syntax my-let
   (lambda (x)
     (define-syntax #%app (z))
     10))

There's a hidden `#%app' before the `z'. It's used in a phase 2
position, but captured by the internal definition for `#%app'. The
interally defined `#%app', meanwhile, is for code at phase 1. Using a
phase-N identifier in a phase-M position is an out-of-context error if
N != M.

[Note: different phases have different top-level environments, but
non-top-level bindings capture across phases. So that's how an
out-of-context error is possible.]

> +-----------------------------------------------------
> Finally I tried this:
> 
> (define-syntax my-let
>   (lambda (x)
> 
>     (define-syntax with-values
>       (lambda (x)
>         (define-syntax #%app
>           (lambda (x)
>             (syntax-case x ()
>               [(_ rator rands ...) #'(apply rator rands ...)])))
>         (syntax-case x ()
>           [(_ P C) #'(call-with-values (lambda () P) C)])))
> 
>     (syntax-case x ()
>       [(_ () body) #'((lambda () body))]
>       [(_ ((var* val*) ...) body)
>        #'((lambda (var* ...) body) val* ...)])))
> 
> And I get the seemingly contradictory error message:
> 
> compile: bad syntax; function application is not allowed, because no #%app
> syntax transformer is bound in: (lambda (x) (define-syntax #%app (lambda (x)
> (syntax-case x () ((_ rator rands ...) (syntax (apply rator rands ...))))))
> (syntax-case x () ((_ p c) (syntax (call-with-values (lambda () p) c)))))

Similar to above, except that this time there is no internal definition
of `#%app' to capture the hidden `#%app' in front of `lambda'. Also,
there's no top-level definition of `#%app' in phase 2. So the phase-2
`#%app' is free.

Matthew