1 \section{Library {\tt H}: hygienic macros}
3 \paragraph{Warning} This hygienic macro system is quite new and
4 experimental. Its API is likely to evolve over the next versions of
5 metalua. Feedbacks are especially welcome.
7 A common problem with meta-programming tools is variable capture, and
8 the art of automatically avoiding capture is called hygiene. the {\tt
9 H} library automates as much as possible the handling of hygiene.
11 The design of H tries to respect metalua's core principles:
13 \item Nothing taboo under the hood: the underlying mechanisms of the
14 language must remain simple enough to be intelligible to their
15 intended users. Black magic should be banned from the desing. This
16 rules out hygienic macros as a primitive: these either rely on very
17 advanced and hard to predict mechanisms, or severely limit the
18 manipulation tools available to the macro authors.
19 \item Simple by default: advanced users should know what happens under
20 the hood, but more casual users should be able to simply turn the
21 ignition and drive. It should be possible to use H, for regular
22 macros, without much understanding of its advanced principles and
24 \item Everything's a regular program: again, most macro systems
25 offering hygiene limit macro manipulations to a term rewriting
26 framework, which might be Turing complete, but makes many advanced
27 stuff cumbersome to write. AST are regular data, which must be
28 manipulable by regular programs.
29 \item Extension: metalua tries to offer the most extensible possible
30 language to its users. If the author of the language couldn't
31 implement a feature as a regular extension, it would probably
32 outline a severe limitation of the system.
35 \paragraph{Inside captures}
36 There are two kind of captures, inside a macro and outside a
37 macro. People often think about inside captures, in parts because the
38 C preprocessor is subject to it. It happens when a macro inserts user
39 code in a quote, and the quote declares a local variable that shadows
45 function dollar.TRICOND(cond, iftrue, iffalse)
46 local code = +{ block:
49 if tmp then result = -{iftrue}
50 else result = -{iffalse} end }
51 return `Stat{ code, +{result} }
55 local foo = $TRICOND(tmp%2==0, "even", "odd")
58 Here, the \verb|tmp| local variable used in the macro code captures
59 the user's one, and cause a failure (an attempt to get the modulus of
60 \verb|nil|). The expanded code looks like:
63 local foo = -{ `Stat{ +{ block:
66 if tmp then result = "even"
67 else result = "odd" end
71 This is fixed by renaming automatically all local variables in the
72 macro with fresh names. H provides an AST walker which does
73 that. However, it needs to rename only macro code, not user-provided
74 code; therefore the macro writer has to somehow mark the user
75 code. This is done with the ``!'' prefix operator: sub-trees marked
76 with ``!'' won't experience any renaming. The following version is
77 therefore safe w.r.t. inside variable capture:
82 function dollar.TRICOND(cond, iftrue, iffalse)
83 local code = +{ block:
86 if tmp then result = -{!iftrue}
87 else result = -{!iffalse} end }
88 return H(`Stat{ code, +{result} })
92 local foo = $TRICOND(tmp%2==0, "even", "odd")
99 local foo = -{ `Stat{ +{ block:
100 local -{`Id '.1.L.tmp'}, -{`Id '.2.L.result'} -- new fresh names
101 -{`Id '.1.L.tmp'} = tmp%2==0 -- no capture!
102 if -{`Id '.1.L.tmp'} then -{`Id '.2.L.result'} = "even"
103 else -{`Id '.2.L.result'} = "odd" end
104 }, `Id '.2.L.result' } }
107 \paragraph{Outside captures}
108 We've seen that macros can capture the user's variables; but the
109 opposite can also happen: the user can capture the variables required
115 dollar.log = |x| +{ printf("%s = %s",
116 -{table.tostring(x)},
117 table.tostring(-{x})) } }
118 local x = { 1, 2, 3 }
119 \$log(x) -- prints "`Id 'x' = { 1, 2, 3 }"
124 printf("%s = %s", "`Id 'x'", table.tostring(x))
127 Now, replace "x" with "table", and you get an outside capture: "local
128 table = { 1, 2, 3 } shadows the table module from which the macro
129 tries to get table.tostring().
131 The most widespread language which supports non-hygienic macros,
132 Common Lisp, deals with that issue by being a Lisp-2: it has separate
133 namespaces for functions and ``normal'' variables. This happens to
134 remove many common capture cases.
136 H fixes this by renaming the free variables in hygienized
137 macros. After this, a "local new\_names = old\_names" statement is
138 generated, which re-establishes the correspondance between
139 names. Let's make the examples above hygienic:
144 dollar.log = |x| H+{ printf("%s = %s",
145 -{!table.tostring(x)},
146 table.tostring(-{!x})) } }
147 local table = { 1, 2, 3 }
148 \$log(table) -- prints "`Id 'table' = { 1, 2, 3 }"
151 The code above expands into:
154 local table = { 1, 2, 3 }
155 (-{`Id '.1.X.printf'}) ("%s = %s",
157 (-{`Id '.2.X.table'}.tostring(table)))
160 To make this work, we need to introduce, somewhere where no variable
161 is captured, the following local statement:
163 local -{`Id '.1.X.printf'}, -{`Id '.2.X.table'} = printf, table
166 The correct way would be to let the user decide where this statement