]> git.lizzy.rs Git - metalua.git/blob - doc/manual/hygiene-ref.tex
fix CRLF
[metalua.git] / doc / manual / hygiene-ref.tex
1 \section{Library {\tt H}: hygienic macros}
2
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.
6
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.
10
11 The design of H tries to respect metalua's core principles:
12 \begin{itemize}
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
23   implementation.
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.
33 \end{itemize}
34
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
40 a user one:
41
42 \begin{verbatim}
43 -{ block:
44    require 'dollar'
45    function dollar.TRICOND(cond, iftrue, iffalse)
46       local code = +{ block:
47          local tmp, result
48          tmp = -{cond}
49          if tmp then result = -{iftrue} 
50          else result = -{iffalse} end }
51       return `Stat{ code, +{result} }
52    end }
53
54 local tmp = 5
55 local foo = $TRICOND(tmp%2==0, "even", "odd")
56 \end{verbatim}
57
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:
61 \begin{Verbatim}
62 local tmp = 5
63 local foo = -{ `Stat{ +{ block:
64    local tmp, result
65    tmp = tmp%2==0
66    if tmp then result = "even"
67    else result = "odd" end
68 }, +{result} } }
69 \end{Verbatim}
70
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:
78
79 \begin{verbatim}
80 -{ block:
81    require 'dollar'
82    function dollar.TRICOND(cond, iftrue, iffalse)
83       local code = +{ block:
84          local tmp, result
85          tmp = -{!cond}
86          if tmp then result = -{!iftrue} 
87          else result = -{!iffalse} end }
88       return H(`Stat{ code, +{result} })
89    end }
90
91 local tmp = 5
92 local foo = $TRICOND(tmp%2==0, "even", "odd")
93 \end{verbatim}
94
95 It expands to:
96
97 \begin{Verbatim}
98 local tmp = 5
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' } }
105 \end{Verbatim}
106
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
110 by a macro:
111
112 \begin{Verbatim}
113 -{ block:
114    require 'dollar'
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 }"
120 \end{Verbatim}
121
122 This expands to:
123 \begin{Verbatim}
124 printf("%s = %s", "`Id 'x'", table.tostring(x))
125 \end{Verbatim}
126
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().
130
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. 
135
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:
140
141 \begin{Verbatim}
142 -{ block:
143    require 'dollar'
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 }"
149 \end{Verbatim}
150
151 The code above expands into:
152
153 \begin{Verbatim}
154 local table = { 1, 2, 3 }
155 (-{`Id '.1.X.printf'}) ("%s = %s",
156                         "`Id 'table'",
157                         (-{`Id '.2.X.table'}.tostring(table)))
158 \end{Verbatim}
159
160 To make this work, we need to introduce, somewhere where no variable
161 is captured, the following local statement:
162 \begin{Verbatim}
163 local -{`Id '.1.X.printf'}, -{`Id '.2.X.table'} = printf, table
164 \end{Verbatim}
165
166 The correct way would be to let the user decide where this statement
167 should go.
168