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