[Template-haskell] Hygienic macros and lexical-scoping

Simon Peyton-Jones simonpj at microsoft.com
Wed Feb 25 09:31:48 EST 2004


Kamil

Thanks for your email.  

| My question is: when binding of variables is resolved? Is it during
| translation of quotation, or maybe during expansion of $(...) where
the above
| code is used?

Answer 1: Binding is resolved before any expansion or translation.  So
in

	\x -> $(f [| x |])

the occurrence of 'x' is bound indissolubly to the binding '\x', and you
can tell that by looking at the source code, before expansion.  So if
'f' was defined like this
	f :: Q Exp -> Q Exp
	f e = [| \x -> $e |]

Then the expression
	\x -> $(f [| x |])
means the same as
	\x1 -> \x -> x1

Answer 2: In an extension that is implemented but not described in a
published paper (but is described here
http://research.microsoft.com/~simonpj/tmp/notes2.ps) we also support a
sort of dynamic binding:

	\x -> $(f (dyn "x"))

Here 'dyn' is a function with type
	dyn :: String -> Q Exp

(dyn "x") is a variable that will bind to whatever "x" is in scope where
that expression is spliced in.  So the expression
	\x -> $(f (dyn "x"))
means the same as
	\x1 -> \x -> x


Notice that this dynamic binding always explicitly involves a string,
like "x".  You wouldn't expect (dyn "x") to bind indissolubly to the
enclosing '\x'.


| After reading the paper, I'm convinced that it's former case. But
let's
| consider:
| using x val body = [| do { (var x) <- val; body; (var x).Dispose () }
|]
| // x is of type string
| 
| [| \y -> do { $(using "x" [| x |]); f y |]
| 
| wouldn't work, and one would have to use gensym by his own to get
expected
| sematics.

I'm sorry, I didn't understand this at all.  What is 'using x val body'?
What is .Dispose()?  Not Template Haskell certainly.

| In our system we are shifting binding of variables from quotation
translation
| to macro expansion phase, so [| x |] can be "catched" inside 'using'
| meta-function. Resolving of scoping is done after all executions of
macros
| are done. It involves quite complex algorithm to tag variables with
place
| where they come from - we are adapting Dybvig's algorithm into our
system, so
| we could use most flexible convention introduced by Scheme community.

As I say above, TH supports both pre-expansion binding (via plain
mentions of a variable), and post-expansion biding (via "dyn"). Whatever
you choose to do, let me encourage you to give it a formal description.

| As we discussed our design, there was some confusion if macros should
| introduce new definitions into program. I noticed, that in Haskell it
is done
| by special keyword 'splice', and it's forbidden inside quotations. As
we
| decided to resolve names in late phase, we could introduce definitions
in
| arbitrary place. 

Yes, that's right.  We discuss this in Section 8 of the above note.
(But nested declaration splicing isn't yet implemented.)


Some questions arise in that case:
| Let's consider following macros (in Nemerle syntax, as I'm more used
to it)
| 
| using Foo;   /// it introduces functions 'blah' and 'bar' variables
| macro m() { <[ def bar() { 1 } ]> }
| macro m'() { <[ m(); blah() + bar() ]> }
| 
| does m'() generate
| def bar_43 = 1; Foo.blah() + Foo.bar()
| or
| def bar_43 () { 1 }; Foo.blah() + bar_43()

The most systematic story in TH would be that you get the former --
that's lexical scoping.  You could get the latter by using (dyn "bar").
But, for top-level declarations, it's very inconvenient to use all the
dyn stuff.  So in TH we compromise: top level splices lexically shadow
previous stuff, so you get the latter definition.

We explicitly call this a hack (see section 8.2).

Simon



More information about the template-haskell mailing list