[e-lang] Killing the MatchBindExpr

Mark S. Miller markm at cs.jhu.edu
Wed May 3 02:31:43 EDT 2006


The following is based on an idea of Dean's, as refined (or at least mutated) 
by Alan, MarcS, Tyler, and me.

Dean observes that the MatchBindExpr is generally useful only when used as the 
conditional in a conditional flow of control context like an 'if'. When used 
in this context, it is also not problematic. The problems come from making the 
MatchBindExpr into a full fledged expression, merely in order to use it as the 
conditional in this context. By being a full fledged expression which returns 
a boolean, Kernel-E then has to cope with the consequences of control flow 
continuing past a match failure, into the code where the variables defined by 
the pattern are in scope, but where they haven't been bound by a successful 
pattern match. To reiterate a previous example:

      ? [1,2] =~ [x, y :char]
      # value: false

      ? y
      # value: <ref broken by problem: <ClassCastException: \
      #         Integer doesn't coerce to a Character>>

My previous email failed to point out the worst problem with the above 
behavior: The definition of 'y' is guarded by ':char'. Surely, then, within 
the resulting scope, we can count on 'y' having a character as its value? The 
above counter-example shows that E is currently not faithful to this intuition.

Dean suggests that, instead, Kernel-E should bundle pattern match with if-like 
conditional control flow, combining them into one construct. Here are two 
possibilities, the if-match and the extended-switch, either of which could 
replace the current MatchBindExpr in Kernel-E

1) if-match:

    e ::= if (e1) =~ p1 {e2} else {e3}

The if-match would be in addition to the existing 'if' production. As with the 
existing 'if', e2 is evaluated in the scope resulting from the 'if' head, here 
e1 and p1; but e3 is not. In E, we'd allow the normal 'else if' chaining to 
mix these two forms of 'if' without worry. Such chaining would expand into 
Kernel-E nesting just like it does now.

Currently, the E switch expression expands to an 'else if' chain, with 
MatchBindExprs as the conditions of these 'if's. If the if-match replaces 
MatchBindExpr in Kernel-E, then switch could expand to an if-match chain in 
essentially the same way.

Alternatively, we could promote the switch expression into Kernel-E. In order 
for the switch to have all the power needed to express all other match 
constructs, we could add an optEjector-expression argument ('e2' below) to the 
head of the switch expression:

2) extended-switch:

    e ::= switch (e1,e2) { <match pi {ei}>* }

In E, e2 would be optional. If e2 is present, then if all patterns fail to 
match, the diagnostic from the last failure is reported to e2's value. If e2 
is absent, it defaults to null and the last failure is thrown, which is 
consistent with switch's current behavior. By adding the optEjector argument, 
we can distinguish match-failure from a thrown exception.

Since E already contains the switch expression, adding extended-switch to E 
and Kernel-E does not really increase the size of E. If we also remove 
MatchBindExpr from E and Kernel-E, then Kernel-E stays about the same size and 
E actually gets smaller.

Many languages (such as C and Java) have constant-time switch constructs (at 
least when the case values are dense). Currently, if the E programmer writes a 
simple switch which should be compiled to a constant time dispatch, this is 
first  expanded to an 'else if' chain. Although Dan reports that gcc will 
actually optimize some 'else if' chains to constant time dispatches, that's a 
lot to ask of an optimizer. But if the switch is passed through directly to 
Kernel-E, then it should be easier for reasonable E implementations to 
recognize some of the simple cases they can turn into constant time dispatches.

Because of these issues, initially I wanted to just add the extended-switch to 
E and Kernel-E, and forget about the if-match. However, this fantasy did not 
survive the first two actual examples I tried to convert, which are shown 
below. For each, their conversion to if-match is trivial, and the resulting 
code is just as readable and concise. OTOH, their conversion to 
extended-switch is fine as a Kernel-E expansion, but is too verbose to impose 
on the E programmer. If we decide to put extended-switch into Kernel-E, we 
should consider adding if-match to E and expanding it to extended-switch as 
shown below.

E and Kernel-E would then both stay about the same size. Both would shed the 
MatchBindExpr. In exchange, E would take on the if-match and Kernel-E would 
take on the extended-switch. Doing so would cure both languages of the scoping 
irregularities of the current MatchBindExpr.


in webServerAuthor.emaker
original:
                     if (action =~ `GET /@{fileName} HTTP/1.@{trailer}`) {
                         filePath := fileName
                     }
with if-match:
                     if (action) =~ `GET /@{fileName} HTTP/1.@{trailer}` {
                         filePath := fileName
                     }
with switch:
                     switch (action) {
                         match `GET /@{fileName} HTTP/1.@{trailer}` {
                             filePath := fileName
                         }
                         match _ {}
                     }


in darpaBrowser.caplet
original:
             if (text.endsWith("/")) {
                 #...
             } else if (text =~ `http://@host/@path`) {
                 #...
             } else {
                 #...
             }
with if-match:
             if (text.endsWith("/")) {
                 #...
             } else if (text) =~ `http://@host/@path` {
                 #...
             } else {
                 #...
             }
with extended-switch:
             if (text.endsWith("/")) {
                 #...
             } else {
                 escape ej {
                     switch (text, ej) {
                         match `http://@host/@path`) {
                             #...
                         }
                     }
                 } catch _ {
                     #...
                 }
             }

-- 
Text by me above is hereby placed in the public domain

     Cheers,
     --MarkM



More information about the e-lang mailing list