.@ Tony Finch – blog

I've been playing around with Lua recently. It's a really sweet little language. A particular highlight is its "tables", which are generalized associative arrays that can have keys of any type. (Most languages only allow strings and/or numbers.) It also has pretty nice uniform handling of multiple function arguments, multiple return values, and multiple assignment.

A frequent topic of discussion on the Lua list is the fact that Lua does not have a switch/case statement. Most of the people who propose how one should be added tend to copy other languages which can only switch on one value at a time. I think it would me more in keeping with the rest of Lua to have a multi-valued case statement. I also quite like the idea of making it capable of ML-style pattern matching and variable binding.

The syntax I have in mind is as follows. I'm using ? to mean 0 or 1 of the following, * to mean 0 or more, and + to mean 1 or more.

    case explist1
+(+(match patlist
      ?(if exp) )
As in C, multiple matches for a given block are expressed using multiple match clauses, instead of the alternatives being listed in a single clause as in Modula. Each match can be guarded by a conditional expression; when it evaluates to false the match fails and later ones are tried. The statements following then are evaluated if one of the preceding matches succeeds. You can put a "default" or "otherwise" case after an else. (I quite like the re-use of keywords, though perhaps it is too cute.) All variables bound by the match clauses are visible in the following block; the variables in a pattern list must all be different; if the clause binding a variable isn't the one that matched then the variable's value is nil.

Lua also needs more expressive exception handling - I'm not too fond of its "protected call" mechanism. It would be more palatable if it were dressed up in something like:

+(+(catch patlist
      ?(if exp) )
        block )
        block )

A useful bit of syntactic sugar is to be able to declare functions in a pattern matching style instead of an argument-passing style, so:

    function (...)
        case ...
        match a, b, c
            blah blah
could be more concisely written:
    match a, b, c
        blah blah

I'm still pondering the syntax for pattern lists. A basic principle is that all direct comparisons should be against constants, so that the match can be compiled into a table lookup against a constant table. This means only numbers and strings - other objects in Lua either do not have compile-time expressions (C userdata) or have expressions that do not denote constant values but instead describe how to create new objects at run-time (function closures and tables). I also want to allow indirect comparisons against table elements, so that it's possible to match against structured data. Finally there needs to be a way of binding a variable to the value being matched against.

I do not intend to support exact matches against tables: only the elements that you specify in the match will be checked and if the table has more elements they will be ignored. This is because the extra check to make the match exact may not be entirely trivial to implement, and because it makes code brittle in the presence of unexpected values squirreled away in a table - it's quite common in prototype-based object systems to add methods to an object without co-operation from the code that created it.

So the basic syntax for a pattern to match against simple constants or to bind a variable is:

    String | Number | Name
Matching against the first few integer indexed elements of a table in list style is also simple:
    "{" pattern *( "," pattern ) "}"

It's also useful to both match against a table and bind a variable, so that you can refer to the table in the later statements. (It isn't possible to reconstruct the table because it may have extra elements, and even if it were possible it would be inefficient and the result would have a different identity.) It's also useful to both match a constant and bind a variable, if you have multiple match clauses: case f() match 3 match 4 then -- did f return three or four? I'm still unsure about the syntax for matching and binding. Some possibilities are:

    pattern and pattern
    pattern pattern
    Name "=" pattern
    Name "==" pattern

The idea of "and" patterns is that you are matching both the left-hand side and the right-hand side. It suggests that there should also be "or" patterns, as in case f() match 3 or 4 then ... In MCPL you get the same effect as and by just writing the patterns side-by-side, and (like sh) | is used instead of or.

The idea of using "=" is to make binding in a pattern look like assignment, whereas "==" is supposed to be like an assertion. However these get problematic when we try to fit key/value style table matching into the syntax. Table constructors in Lua look like:

    tableconstructor = "{" ?fieldlist "}"
    fieldlist = field *(fieldsep field) ?fieldsep
    fieldsep = "," | ";"
    field = "[" exp "]" "=" exp
        | Name "=" exp
        | exp
(The first form of field is by analogy with the table[key] form of index expression. The second is like table.name indexing, which is syntactic sugar for table["name"]. The last is for list-style construction, where the value is assigned to the next integer index starting from 1.)

If we follow constructor syntax closely, we get the serious difficulty that a pattern like { foo = bar } isn't clear which name is the table index and which is the variable being bound. This probably indicates that = should be avoided.

Perhaps the solution is to use an arrow to indicate which way values flow, as in:

    tablepattern = "{" ?fieldpatterns "}"
    fieldpatterns = fieldpat *(fieldsep fieldpat) ?fieldsep
    fieldpat = "[" (String | Number) "]" "->" pattern
        | Name "->" pattern
        | pattern
    pattern = String | Number | Name
        | pattern and pattern
        | pattern or pattern
This seems reasonably OK, I think. It also suggests adding a free-standing match operator -> that produces a boolean result (and probably should not bind variables).

It makes me want to change the assignment operator to <- for symmetry :-) But fiddling with Lua's existing features is the topic for another post.