]> git.lizzy.rs Git - metalua.git/blob - doc/manual/sample-match.tex
73c9ebafee265f4a82f0d1e00c63b4200d213c3b
[metalua.git] / doc / manual / sample-match.tex
1 \subsection{Structural pattern matching}\r
2 \r
3 FIXME: refer to the official match extension instead of re-explaining\r
4 what pattern matching is about.\r
5 \r
6 \subsubsection{Basic principles}\r
7 In many languages, including Lua, ``pattern matching'' generally\r
8 refers to the ability to:\r
9 \begin{itemize}\r
10 \item Analyse the general shape of a string;\r
11 \item capture specified parts of it into temporary variables\r
12 \item do some computation when the string matches a certain pattern,\r
13   generally using the temporary captured variables.\r
14 \end{itemize}\r
15 \r
16 In languages derived from ML\footnote{for instance OCaml, SML, Haskell\r
17   or Erlang.}, pattern matching can be done on arbitrary data, not\r
18 only strings. One can write patterns which describe the shape of a\r
19 data structure, bind some interesting sub-parts of it to variables,\r
20 and execute some statements associated with a given pattern whenever\r
21 the data matches.\r
22 \r
23 This sample aims to implement this capability into metalua. It will\r
24 discuss:\r
25 \begin{itemize}\r
26 \item How to describe data patterns (it turns out that we'll hijack\r
27   metalua's expression parser, which is a great time-saving trick when\r
28   it can be pulled).\r
29 \item How to compile them into equivalent code.\r
30 \item How to wrap all this up into a working compiled statement\r
31 \item What improvements can be done to the proposed design.\r
32 \end{itemize}\r
33 \r
34 \subsubsection{Patterns}\r
35 A match statement consists of a tested term, and a series of (pattern,\r
36 block) pairs. At execution, the first pair whose pattern accurately\r
37 describes the tested term is selected, and its corresponding block is\r
38 evaluated. Other blocks are ignored. \r
39 \r
40 We will keep the implementation of patterns as simple as possible. To\r
41 do that, we will put a first constraint: the AST of a pattern must be\r
42 a legal expression AST. This way, we save most of the syntax handling\r
43 trouble. More specifically:\r
44 \r
45 \begin{itemize}\r
46 \item numbers are valid patterns, which only match identical numbers;\r
47 \item strings are valid patterns, which only match identical strings;\r
48 \item tables are valid patterns if all of their keys are integers or\r
49   strings, and all of their values are valid patterns. A table pattern\r
50   matches a tested term if:\r
51   \begin{itemize}\r
52   \item the tested term is a table;\r
53   \item every key that appears in the pattern also appears in the\r
54     tested term;\r
55   \item for every key, the value associated to this key in the tested\r
56     term is matched by the sub-pattern associated to this key in the\r
57     pattern;\r
58   \end{itemize}\r
59 \item variables are valid patterns, and match everything. Moreover,\r
60   the term matched by the variable captures it, i.e. in the\r
61   corresponding block, that variable is set to the\r
62   matched term.\r
63 \end{itemize}\r
64 \r
65 Notice that since \verb|tag| is a regular field in metalua (albeit\r
66 with an optional dedicated syntax), it doesn't need a special case in\r
67 our pattern definition.\r
68 \r
69 \paragraph{Example} Let's consider implementing an evaluator for\r
70 Metalua expressions. We will restrict ourselves to binary operators,\r
71 variables and numbers; we will also consider that we have a\r
72 \verb|values| table mapping variable names to their value, and\r
73 \verb|binopfuncs| which associate an operator name with the corresponding\r
74 function. The evaluator would be written:\r
75 \r
76 \begin{Verbatim}[fontsize=\scriptsize]\r
77 \r
78 function eval(t)\r
79    match t with\r
80    | `Op{ op, a, b } -> return binopfuncs[op](eval(a), eval(b))\r
81    | `Number{ n }    -> return n\r
82    | `Variable{ v }  -> return values[v]\r
83    end\r
84 end\r
85 \end{Verbatim}\r
86 \r
87 \subsubsection{Pattern compilation}\r
88 \r
89 A pattern in a case will translate into:\r
90 \begin{itemize}\r
91 \item tests: testing that the type of the tested case is the same as\r
92   the pattern's type for numbers, strings and tables;\r
93 \item local declarations: a variable must be bound to the\r
94   tested term's value;\r
95 \item nested tests for every pattern key/value pair, when the pattern\r
96   is a table. Moreover, since there might be multiple tests to run\r
97   against the tested term's field, it should be put in a temporary\r
98   variable.\r
99 \end{itemize}\r
100 \r
101 For instance, consider the following pattern:\r
102 \r
103 \begin{Verbatim}[fontsize=\scriptsize]\r
104 \r
105 { tag = "Foo", 10, { 20 }, { x = a }, b }\r
106 \end{Verbatim}\r
107 \r
108 It corresponds to the series of tests and assignments on the left:\r
109 \r
110 \begin{minipage}{6cm}\r
111 \begin{Verbatim}[fontsize=\scriptsize]\r
112 \r
113 let v1 = <tested_term>\r
114 type(v1) == "table"\r
115 let v2 = v1.tag\r
116 v2 ~= nil\r
117 type(v2) == "string"\r
118 v2 == "Foo"\r
119 let v2 = v1[1]\r
120 v2 ~= nil\r
121 type(v2) == "number"\r
122 v2 == 10\r
123 let v2 = v1[2]\r
124 v2 ~= nil\r
125 type(v2) == "table"\r
126 let v3 = v2[1]\r
127 v3 ~= nil\r
128 type(v3) == "number"\r
129 v3 == 20\r
130 let v2 = v1[3]\r
131 v2 ~= nil\r
132 type(v2) == "table"\r
133 let v3 = v2.x\r
134 v3 ~= nil\r
135 local a = v3\r
136 let v2 = v1[4]\r
137 v2 ~= nil\r
138 local b = v2\r
139 \r
140 \r
141 \end{Verbatim}\r
142 \end{minipage}\r
143 \begin{minipage}{6cm}\r
144 \begin{Verbatim}[fontsize=\scriptsize]\r
145 \r
146 let v1 = tested_term\r
147 if type(v1) == "table" then\r
148  let v2 = v1.tag\r
149  if v2 ~= nil then\r
150   if type(v2) == "string" then\r
151    if v2 == "Foo" then\r
152     let v2 = v1[1]\r
153     if v2 ~= nil then\r
154      if type(v2) == "number" then\r
155       if v2 == 10 then\r
156        let v2 = v1[2]\r
157        if v2 ~= nil then\r
158         if type(v2) == "table" then\r
159          let v3 = v2[1]\r
160          if v3 ~= nil then\r
161           if type(v3) == "number" then\r
162            if v3 == 20 then\r
163             let v2 = v1[3]\r
164             if v2 ~= nil then\r
165              if type(v2) == "table" then\r
166               let v3 = v2.x\r
167               if v3 ~= nil then\r
168                local a = v3\r
169                let v2 = v1[4]\r
170                if v2 ~= nil then\r
171                 local b = v2\r
172                 <inner_term>\r
173 end ... end -- (16 times)\r
174 \end{Verbatim}\r
175 \end{minipage}\r
176 ~\\~\\\r
177 \r
178 Notice that the relative order of tests and assignments is meaningful:\r
179 we cannot put all assignments on one side, and all tests on an\r
180 other, e.g. \verb|v2 = v1.tag| on line 3 doesn't make sense if\r
181 \verb|type(v1) == table| on line 2 fails.\r
182 \r
183 We will compile patterns in several steps: first, accumulate all the\r
184 tests and assignments in order; then, we will collapse them in a\r
185 single big nesting of ``if'' statements. At first, we won't try\r
186 to optimize the form of the final term. The list above left would be\r
187 collapsed into the single compound statement above on the right.\r
188 \r
189 \paragraph{Accumulating constraints and tests}\r
190 This is done by a \verb|parse_pattern()| function, which does just what\r
191 is described in the bullet list above:\r
192 \r
193 \begin{Verbatim}[fontsize=\scriptsize]\r
194 \r
195       -------------------------------------------------------------------\r
196       -- Turn a pattern into a list of conditions and assignations,\r
197       -- stored into [acc]. [n] is the depth of the subpattern into the\r
198       -- toplevel pattern; [tested_term] is the AST of the term to be \r
199       -- tested; [pattern] is the AST of a pattern, or a subtree of that\r
200       -- pattern when [n>0].\r
201       -------------------------------------------------------------------\r
202       local function parse_pattern (n, pattern)\r
203          local v = var(n)\r
204          if "Number" == pattern.tag or "String" == pattern.tag then\r
205             accumulate (+{ -{v} == -{pattern} })\r
206          elseif "Id" == pattern.tag then\r
207             accumulate (+{stat: local -{pattern} = -{v} })\r
208          elseif "Table" == pattern.tag then\r
209             accumulate (+{ type( -{v} ) == "table" } )\r
210             local idx = 1\r
211             for _, x in ipairs (pattern) do\r
212                local w = var(n+1)\r
213                local key, sub_pattern\r
214                if x.tag=="Key" \r
215                then key = x[1];           sub_pattern = x[2]\r
216                else key = `Number{ idx }; sub_pattern = x; idx=idx+1 end\r
217                accumulate (+{stat: (-{w}) = -{v} [-{key}] })\r
218                accumulate (+{ -{w} ~= nil })\r
219                parse_pattern (n+1, sub_pattern)\r
220             end\r
221          else error "Invalid pattern type" end\r
222       end\r
223       -------------------------------------------------------------------\r
224 \end{Verbatim}\r
225 \r
226 This function relies on the following helper functions:\r
227 \begin{itemize}\r
228 \item {\tt var($n$)} generates the variable name ``\verb|v|$n$'', which\r
229   is used to store the tested term at depth level $n$. Indeed,\r
230   sub-patterns in table fields are matched against sub-terms of the\r
231   tested term. It also remembers of the biggest $n$  it ever received, \r
232   and stores it into \verb|max_n| (this will be used to know which\r
233   local vars have to be generated, see below);\r
234 \item {\tt accumulate()} just stores an additional code snippet in a\r
235   dedicated list;\r
236 \end{itemize}\r
237 \r
238 In the quotes, you might notice the parentheses around the variable in\r
239 ``\verb|(-{w}) = -{v} [-{key}]|'': they're here to let the compiler\r
240 know that what's in \verb|-{...}| is an expression, not a\r
241 statement. Without them, it would expect {\tt w} to be the AST of a\r
242 statement (since we're in a statement context at this point), then\r
243 choke on the unexpected ``='' symbol. This is a common idiom, which\r
244 you should think about everytime you generate an assignment to an\r
245 anti-quoted identifier.\r
246 \r
247 \paragraph{Collapsing the accumulated quotes}\r
248 As written above, our collapsing function will be kept as simple\r
249 as possible, and will not try to minimize the amount of generated\r
250 code. It takes as parameters {\tt n}, the index of the quote currently\r
251 collapsed in the accumulator, and {\tt inner\_term} the statement\r
252 block to put inside the innermost part of the test. It calls itself\r
253 recursively, so that the collapsed term is built inside out (generally\r
254 speaking, working with trees, including AST, involves a lot of\r
255 recursive functions). \verb|acc| is the list filled by the\r
256 \verb|accumulate()| function:\r
257 \r
258 \begin{Verbatim}[fontsize=\scriptsize]\r
259 \r
260       -------------------------------------------------------------------\r
261       -- Turn a list of tests and assignations into [acc] into a\r
262       -- single term of nested conditionals and assignments.\r
263       -- [inner_term] is the AST of a term to be put into the innermost\r
264       -- conditionnal, after all assignments. [n] is the index in [acc]\r
265       -- of the term currently parsed.\r
266       -- \r
267       -- This is a recursive function, which builds the inner part of\r
268       -- the statement first, then surrounds it with nested \r
269       -- [if ... then ... end], [local ... = ...] and [let ... = ...]\r
270       -- statements.\r
271       -------------------------------------------------------------------\r
272       local function collapse (n, inner_term)\r
273          assert (not inner_term.tag, "collapse inner term must be a block")\r
274          if n > #acc then return inner_term end\r
275          local it = acc[n]\r
276          local inside = collapse (n+1, inner_term)\r
277          assert (not inside.tag, "collapse must produce a block")\r
278          if "Op" == it.tag then \r
279             -- [it] is a test, put it in an [if ... then .. end] statement\r
280             return +{block: if -{it} then -{inside} end }\r
281          else \r
282             -- [it] is a statement, just add it at the result's  beginning.\r
283             assert ("Let" == it.tag or "Local" == it.tag)\r
284             return { it, unpack (inside) }\r
285          end\r
286       end\r
287 \end{Verbatim}\r
288 \r
289 To fully understand this function, one must remember that test\r
290 operations are translated into {\tt`Op\{ <opname>, <arg1>, <arg2>\}}\r
291 AST. That's why we didn't have to put an explicit reminder in the\r
292 accumulator, remembering whether a quote was a test or a statement:\r
293 we'll just check for \verb|`Op|'s presence.\r
294 \r
295 \subsubsection{Match statement compilation}\r
296 We already know how to translate a single pattern into a Lua test\r
297 statement. This statement will execute a given block, with all pattern\r
298 variables bound appropriately, if and only if the tested term matches\r
299 the pattern.\r
300 \r
301 To build a complete match statement, we need to test all patterns in\r
302 sequence. When one of them succeeds, we must skip all the following\r
303 cases. We could do that with some complex ``\verb|else|'' parts in the\r
304 tests, but there is a simpler way to jump out of some nested blocks:\r
305 the ``\verb|break|'' statement. Therefore, we will enclose the cases\r
306 into a loop that executes exactly once, so that a ``\verb|break|''\r
307 will jump just after the last case. Then, we will add a\r
308 ``\verb|break|'' at the end of every statement that doesn't already\r
309 finish with a ``\verb|return|'', so that other cases are skipped upon\r
310 successful matching. To sum up, we will translate this:\r
311 \r
312 \begin{Verbatim}[fontsize=\scriptsize]\r
313 \r
314 match <foo> with\r
315 | <pattern_1> -> <x1>\r
316 | <pattern_2> -> <x2>\r
317   ...\r
318 | <pattern_n> -> <xn>\r
319 end\r
320 \end{Verbatim}\r
321 \r
322 \noindent into this:\r
323 \r
324 \begin{Verbatim}[fontsize=\scriptsize]\r
325 \r
326 repeat\r
327   local v1, v2, ... vx -- variables used to store subpatterns\r
328   let v1 = <tested_term>\r
329   if <compilation of pattern_1> ... then x1; break end\r
330   if <compilation of pattern_2> ... then x2; break end\r
331   ...\r
332   if <compilation of pattern_n> ... then xn; break end\r
333 until true\r
334 \end{Verbatim}\r
335 \r
336 First, we add final \verb|break| statements where required, and we\r
337 compile all (pattern, block) pairs:\r
338 \r
339 \begin{Verbatim}[fontsize=\scriptsize]\r
340 \r
341       -------------------------------------------------------------------\r
342       -- parse all [pattern ==> block] pairs. Result goes in [body].\r
343       -------------------------------------------------------------------\r
344       local body = { }\r
345       for _, case in ipairs (cases) do\r
346          acc = { } -- reset the accumulator\r
347          parse_pattern (1, case[1], var(1)) -- fill [acc] with conds and lets\r
348          local last_stat = case[2][#case[2]]\r
349          if last_stat and last_stat.tag ~= "Break" and \r
350             last_stat.tag ~= "Return" then\r
351             table.insert (case[2], `Break) -- to skip other cases on success\r
352          end\r
353          local compiled_case = collapse (1, case[2])\r
354          for _, x in ipairs (compiled_case) do table.insert (body, x) end\r
355       end\r
356 \end{Verbatim}\r
357 \r
358 \noindent Then, we can just splice it into the appropriate quote:\r
359 \r
360 \begin{Verbatim}[fontsize=\scriptsize]\r
361 \r
362       -------------------------------------------------------------------\r
363       local local_vars = { }\r
364       for i = 1, max_n do table.insert (local_vars, var(i))  end\r
365       \r
366       -------------------------------------------------------------------\r
367       -- cases are put inside a [repeat until true], so that the [break]\r
368       -- inserted after the value will jump after the last case on success.\r
369       -------------------------------------------------------------------\r
370       local result = +{ stat: \r
371          repeat\r
372             -{ `Local{ local_vars, { } } }\r
373             (-{var(1)}) = -{tested_term}\r
374             -{ body }\r
375          until true }\r
376       return result\r
377       -------------------------------------------------------------------\r
378 \end{Verbatim}\r
379 \r
380 There is one point to notice in this quote: \verb|body| is used where\r
381 a statement is expected, although it contains a {\em list} if\r
382 statements rather than a single statement. Metalua is designed to\r
383 accept this, i.e. if {\tt a, b, c, d} are statements, AST {\tt\r
384 `Do\{  a, b, c, d \}} and {\tt`Do\{ a, \{ b, c \}, d\} } are\r
385 equivalent. This feature partially replaces Lisp's \verb|@(...)|\r
386 operator.\r
387 \r
388 \subsubsection{Syntax extension}\r
389 To use this, we provide a syntax inspired by OCaml\footnote{It is\r
390   actually the same syntax as OCaml's, except that we introduced an\r
391   explicit {\tt end} terminator, to stay homogeneous with Lua.}: \r
392 \r
393 \begin{Verbatim}[fontsize=\scriptsize]\r
394 \r
395 match <foo> with\r
396   <pattern> -> block\r
397 | <pattern> -> block\r
398   ...\r
399 | <pattern> -> block\r
400 end\r
401 \end{Verbatim}\r
402 \r
403 For this, we need to declare new keywords \verb|match|,\r
404 \verb|with| and \verb|->|. Then, we build the (pattern, block) parser\r
405 with \verb|gg.sequence{ }|, and read a list of them, separated with\r
406 ``\verb+|+'', thanks to \verb|gg.list{ }|. Moreover, we accept an\r
407 optional ``\verb+|+'' before the first case, so that all cases line up\r
408 nicely:\r
409 \r
410 \begin{Verbatim}[fontsize=\scriptsize]\r
411 \r
412 ----------------------------------------------------------------------\r
413 mlp.lexer:add{ "match", "with", "->" }\r
414 \r
415 mlp.stat:add{ "match", mlp.expr, "with", gg.optkeyword "|",\r
416               gg.list{ gg.sequence{ mlp.expr, "->", mlp.block },\r
417                        separators  = "|",\r
418                        terminators = "end" },\r
419               "end",\r
420               builder = |x| match_parser (x[1], x[3]) }\r
421 ----------------------------------------------------------------------\r
422 \end{Verbatim}\r
423 \r
424 \noindent Now, if you try this\ldots\ it won't work! Indeed, Metalua\r
425 needs to know what keywords might terminate a block of statements. In\r
426 this case, it doesn't know that ``\verb+|+'' can terminate a block. We\r
427 need therefore to add the following statement:\r
428 \r
429 \begin{Verbatim}[fontsize=\scriptsize]\r
430 \r
431 mlp.block.terminators:add "|"\r
432 \end{Verbatim}\r
433 \r
434 \noindent Finally that's it, we have implemented a working pattern\r
435 matching system in 75 lines of code!\r
436 \r
437 \subsubsection{Possible improvements}\r
438 Here are a couple of suggestions to further improve the pattern\r
439 matching system presented above. Some of these proposals can be\r
440 implemented very quickly, some others more complex; all of them\r
441 present some practical interest.\r
442 \r
443 The code of the basic version, as presented here, is available at\r
444 \url{http://metalua.luaforge.net/FIXME}.\r
445 \r
446 \paragraph{Booleans} Boolean constants aren't handled in the system\r
447 above, neither as table keys nor as patterns. They're easy to add, and\r
448 doing it will help you get gently into the code.\r
449 \r
450 \paragraph{Gotos considered beneficial} Gotos might be harmful in\r
451 hand-written programs, but they're a bliss for machine-generated\r
452 code. They would slightly simplify the code of pattern matching as\r
453 presented above; but for many extension proposals listed below, they\r
454 will make reasonnably easy some things which would otherwise be\r
455 awfully contrived. Exercice: simplify the implementation above as much\r
456 as possible by using gotos.\r
457 \r
458 Labels and gotos in metalua ASTs are represented as {\tt`Label\{ id\r
459   \}} and {\tt`Goto\{ id \}} respectively, with {\tt id} an\r
460 identifier, typically generated by {\tt mlp.gensym()}. It is always\r
461 safe to jump out of a block; jumping into a block is not guaranteed\r
462 against weird interactions with local variables and upvalues.\r
463 \r
464 \paragraph{{\tt collapse()} optimization} Instead of nesting if\r
465 statements systematically, two nested {\tt if}s without {\tt else}\r
466 branches can be simplified in a single branch with an {\tt and}\r
467 operator. Not sure it would change the bytecode's efficiency, but\r
468 that's a good exercice of AST manipulation.\r
469 \r
470 \paragraph{Superfluous assignments} When parsing a table entry, we\r
471 assign it to a variable, then recursively call {\tt parse\_pattern()}\r
472 on it; in the frequent case where the entry was simply a variable, it\r
473 re-assigns it again. This could be optimized away.\r
474 \r
475 \paragraph{Checking table arity} In many cases, it is practical to\r
476 check the number of elements in the array-part of the table. Here is a\r
477 simple modification proposal: by default, a table pattern matches a\r
478 table term only if they have the same number of array-part\r
479 elements. However, if the last element of the pattern is {\tt`Dots}\r
480 (a.k.a. {\tt+\{...\}}), then the term simply has to have \r
481 {\it at least} as many array-part elements as the pattern.\r
482 \r
483 \paragraph{Adding guards}\r
484 It is sometimes desirable to add arbitrary conditions for a pattern to\r
485 match, conditions which might no be expressed by a pattern. OCaml\r
486 allows to add them with a ``\verb|when|'' keyword:\r
487 \begin{Verbatim}[fontsize=\scriptsize]\r
488 \r
489 match n with\r
490 | 0               -> print "zero"\r
491 | n when n%2 == 0 -> print "even number"\r
492 | _               -> print "odd number"\r
493 end\r
494 \end{Verbatim}\r
495 I'd advise you to prefer {\tt if} as a dedicated keyword, rather than\r
496 {\tt when}: it's unambiguous in this context, and saves a keyword\r
497 reservation. \r
498 \r
499 \paragraph{More bindings}\r
500 The way pattern matching is currently implemented, one can either bind\r
501 a subterm to a variable, or check its structure against a sub-pattern,\r
502 not both simultaneously. OCaml provides an ``\verb|as|'' operator,\r
503 which allows to do both (Haskell calls it ``\verb|@|''). For instance,\r
504 in the following example, any ADT whose tag is \verb|"RepeatMe"| will\r
505 be replaced by two occurrences of itself, while others will remain\r
506 unchanged:\r
507 \begin{Verbatim}[fontsize=\scriptsize]\r
508 \r
509 match something with\r
510 | `RepeatMe{ ... } as r -> { r, r }\r
511 | x -> x\r
512 end\r
513 \end{Verbatim}\r
514 ``\verb|as|'' will have to be declared as an infix operator, whose\r
515 meaning will remain undefined in expressions which are not patterns.\r
516 \r
517 As an alternative, you can reuse an existing infix operator, thus\r
518 avoiding to mess the expression parser. For instance, use {\tt *}\r
519 instead of {\tt as}. You can go further, and implement {\tt +} as an\r
520 ``or'' operator ({\tt pattern1 + pattern2} would match if either\r
521 of the patterns matches), although this would significantly complicate\r
522 the implementation of {\tt parse\_pattern()}. \r
523 \r
524 The {\tt+} operator might prove tricky to implement, if you don't\r
525 convert your code generator to gotos and labels first.\r
526 \r
527 \paragraph{Linear bindings}\r
528 We should check, when compiling a pattern, that it is left-linear,\r
529 i.e. that variables don't appear more than once in the pattern. People\r
530 might be tempted to write things like this to check whether a tree is\r
531 symmetric:\r
532 \begin{Verbatim}[fontsize=\scriptsize]\r
533 \r
534 match t with\r
535 | `Tree{ x, x } -> print "Symmetric!"\r
536 | `Tree{ x, y } -> print "Not symmetric"\r
537 | `Leaf{ _ }    -> print "Symmetric!"\r
538 end\r
539 \end{Verbatim}\r
540 However, this would work in Prolog but not with our pattern matching,\r
541 as two occurences of the same variable in the pattern don't cause an\r
542 equality test to be added. We should detect such non-linear variables,\r
543 and implement a suitable reaction:\r
544 \begin{itemize}\r
545 \item throw an error, or at least a warning;\r
546 \item or add an equality test between the terms matched by the\r
547   non-linear variable;\r
548 \item or offer a syntax extension that lets the user provide his own\r
549   equality predicate.\r
550 \end{itemize}\r
551 \r
552 Notice that the latter choice would drive us towards a Prolog\r
553 unification algorithm, which opens interesting opportunities.\r
554 \r
555 You might offer an exception for variable ``{\tt\_}'', which is often\r
556 intended as a dummy, unused variable. Non-linear occurences of it\r
557 should probably be silently accepted, without even performing the\r
558 corresponding binding. \r
559 \r
560 \paragraph{Generalized assignments}\r
561 Yet another OCaml-inspired feature: assignments such as\r
562 ``\verb|foo = bar|'', is almost a special\r
563 case of pattern matching with only one case: the left-hand side is\r
564 the pattern, and the right-hand side is the ``raw'' ``\verb|foo=bar|''\r
565 assignment. Seen this way, it allows to write things such as\r
566 ``\verb|`If{ cond, block } = some_ast }|'' to assign \verb|cond| and\r
567 \verb|block| to the subparts of \verb|some_ast| (if we know that\r
568 \verb|some_ast| is the AST of an \verb|if| statement). \r
569 \r
570 If you go this way, however, make sure that the code generated for\r
571 simple {\tt let}s is as efficient as before! Moreover, there is an (easy) \r
572 scoping issue: the variables assigned belong to the scope of the\r
573 surrounding block.\r
574 \r
575 \paragraph{Pattern matchings as expressions}\r
576 Pattern matching are currently statements, and take statements as\r
577 right-hand sides of cases. We could allow pattern matchings where\r
578 expressions are expected: these would take expressions instead of\r
579 statements as right-hand sides. Two ways to implement this: the dirty\r
580 one (hack with functions to change match statements into expressions),\r
581 and the clean one (refactoring existing code, so that it is agnostic\r
582 about its right-hand side type, and provide two specialized\r
583 versions of it for statements and expressions).\r
584 \r
585 \paragraph{Bootstrap it}\r
586 That's something language designers love to do, for largely mystic\r
587 reasons: writing a language's compiler in the language itself. Here,\r
588 the idea is to re-implement the pattern matching extension by using\r
589 pattern matching, and compile it with the older version. Comparing the\r
590 firsrt and second versions of the code will give you an idea of how\r
591 much code clarification is brought to you by the pattern matching\r
592 extension.\r
593 \r
594 \paragraph{Pattern conjunction} Another feature to take from OCaml is\r
595 multiple patterns for a single block. Instead of associating one\r
596 block with one pattern, cases associate a block with a (non-empty)\r
597 list of patterns. All of these patterns have to bond the same\r
598 variables, except for {\tt\_}. The first pattern in the list to match\r
599 the tested term does the binding. Patterns are separated by\r
600 ``\verb+|+''. Example:\r
601 \begin{Verbatim}[fontsize=\scriptsize]\r
602 \r
603 match x with\r
604 | 1 | 2 | 3 -> print(x)\r
605 | n -> print "more than 3"\r
606 end\r
607 \end{Verbatim}\r
608 (Hint: put the block in a local function. $2^{\mathrm{nd}}$ hint: sort\r
609 bound variables, e.g. by lexicographic order. Or much simpler and\r
610 more effective: convert your code generator to gotos+labels first).\r
611 \r
612 \paragraph{XML munching} Ever tried to transform some XML document through\r
613 XSLT? Did you feel that this was even more kludgy than XML itself? Here\r
614 is a challenging proposal:\r
615 \begin{itemize}\r
616 \item Realize, if you didn't already, that Metalua's ADT are\r
617   isomorphic to XML, if you identify string-keys in tables with\r
618   attributes, and limit there content to strings and number. For\r
619   instance, ``{\tt <foo bar=3><baz/>eek</foo>}'' easily maps to ``{\tt\r
620     `foo\{ bar=3, `baz, "eek" \}}'';\r
621 \item compare what ML-style pattern matching does with what XSLT\r
622   does (and with what you'd like it to do);\r
623 \item design, implement, publish. You might want to google\r
624   ``CDuce''\footnote{\url{http://www.cduce.org}} for neat ideas.\r
625 \end{itemize}\r
626 \r
627 If you do this, I'd be really interested to put back your contribution\r
628 in the next version of Metalua!\r
629 \r
630 \subsubsection{Correction}\r
631 Most of the improvements proposed here are actually implemented in the\r
632 {\tt match} library provided with metalua. Check its (commented)\r
633 sources!\r