• Subject: Re: Switch/Case statements revisited
• From: Fabien <fleutot+lua@...>
• Date: Wed, 7 Nov 2007 11:52:23 +0100

I tend to prefer structural pattern matching, as implemented in Haskell, OCaml, SML, Erlang, Prolog etc. It allows among others to express switch cases efficiently, and much more:

match k with
| 10 | 11 -> return 1
| 12 -> return 2
| i if 13<=i and i<=16 -> return 4
end

bewteen the bar and the arrow, you have a pattern, and when a pattern is matched by k, the block after the arrow is executed. The power comes from the flexibility of patterns:

- a number, string, boolean, nil used as a pattern matches values equal to it

- a table matches if every field in the pattern matches the corresponding field in k, which must obviously be a table (I'm simplifying a bit here but that's basically the idea)

- a variable matches everything. More importantly, it *binds* what it matches: in the 3rd case above, i is bound to the value of k in "return 4", so I could have written e.g. "return i+4".

- if a variable appears more than once in the pattern, all values captured by it must be equal. For instance, pattern { x, x } matches { 1, 1 }, but not { 1, 2 } since 1~=2. { x, y } would have matched { 1, 2 } obviously, setting x to 1 and y to 2.

Moreover, you can add guards to a pattern, i.e. a boolean _expression_: a guarded pattern matches only if its guard evaluates to true; the guard can use the variables bound by the guarded pattern. That's how the 3rd case works above: pattern i always succeeds, and variable i is bound to the value of k; then, the guard "13<=i and i<=16" is evaluated.

Obviously, the main interest of pattern matching is to check and decompose complex data structure is a compact and readable way. It's a tool you can't live without when working, for instance, with metalua ASTs. It should be noted that in the current implementation, no closure is ever generated, and some effort has been put into generating efficient code.

A very interesting point in your proposal is the idea of integrating string pattern matching in a more general pattern matching system, this would definitely be worth integrating in metalua's pattern matching. However, there needs to be an easy way to bind the capture. We could recycle the "/" operator, which has no sense in a pattern: foo/"^([a-z]+)([0-9]*)\$" would match strings matched by the regexp on the right, and bind foo to the list of captures. For instance:

match "abc123" with
| c/"^([a-z]+)([0-9]*)\$" -> printf("1st part: %s; 2nd part: %s", c[1], c[2])
end

would print "1st part: abc; 2nd part: 123".

String pattern matching without bindings is easily done with a guard: "| x if x~="MYREGEXP" -> ...".

You might be interested by:

- http://metalua.luaforge.net/metalua-manual.html#htoc73, a tutorial about how to implement a simplified pattern matching extension in metalua

- http://metalua.luaforge.net/src/samples/tutorial-match.lua.html, the corresponding code

- http://metalua.luaforge.net/src/lib/ext-syntax/match.lua.html, the "real", optimized version actually used by metalua

- http://metalua.luaforge.net/src/lib/hygienic.lua.html , an example of a non-trivial use of pattern matching, to implement hygienic macros (it works only partially. Full hygiene raise a couple of tougher problems, which I've only solved on paper for now).