14 Pattern matching unions
Similarly as with tuples, pattern matching is the only way to get
to the value of union. Union patterns consists of field name followed
by nothing, ()
, pattern enclosed in ( )
or tuple pattern.
This mimics ways, one can construct union value.
Following example utilizes pattern matching in simple symbolic expression
computations environment.
union exp {
int Const;
void Var;
*(exp, exp) Add;
*(exp, exp) Sub;
*(exp, exp) Mul;
*(exp, exp) Div;
}
int compute(exp e, int v)
{
switch e {
case Const(x) : return x;
case Var : return v;
case Add(e1, e2) : return compute(e1, v) + compute(e2, v);
case Mul(e1, e2) : return compute(e1, v) * compute(e2, v);
case Div(e1, e2) : return compute(e1, v) / compute(e2, v);
case Sub(e1, e2) : return compute(e1, v) - compute(e2, v);
}
}
exp diff(exp e)
{
switch e {
case Const(_) : return Const(0);
// both Var and Var() are legal
case Var() : return Const(1);
case Add(e1, e2) : return Add(diff(e1), diff(e2));
case Sub(e1, e2) : return Sub(diff(e1), diff(e2));
case Div(e1, e2) :
exp up = Sub(Mul(diff(e1), e2), Mul(e1, diff(e2)));
exp down = Mul(e2, e2);
return Div(up, down);
case Mul(e1, e2) :
return Add(Mul(diff(e1), e2), Mul(e1, diff(e2)));
}
}
14.1 What happen to C's switch and enums?!?
Hmm... they are still there:
union color {
void Red;
void Green;
void Blue;
}
This is roughly equivalent of C's:
typedef enum {
Red,
Green,
Blue
} color
Then we do:
string color_name(color c)
{
string r;
switch (c) {
case Red:
r = "red";
case Green:
r = "green";
// Blue() is also legal
case Blue:
r = "blue";
}
return r;
}
This looks almost like C, modulo one little issue. There is no
fall through. Each case is separate branch or program
execution. One doesn't need to write break statements.
In fact, break would break loop enclosing switch,
if there is any!
14.2 Fall through disclaimer
If you don't like the no-fall-through clause, please first consider
following snippet of code:
switch e {
case Mult(e1, e2):
// here we do fall through
case Const(x):
// hmm... what does x mean, if we fall through from Mult(...)?
// and what can e1 and e2 mean here, if we didn't fall
// through?
}
Because case can bind new variables, it has to introduce
new scope. So fall through is impossible.
14.3 But I want fall through!
Despite disclaimer above limited form of fall-through is available,
if there are no statements after case ...:
.
Only variables bound by all case clauses are available
in clause body. Also each occurrence of given variable needs to
have the same type, thus:
union foo {
int Int;
int Int2;
int Int3;
string String;
bool Bool;
}
foo x;
// this is legal
switch x {
case Bool(i): skip; // don't fall through
case Int(i):
case Int2(i):
case Int3(i): print_int(i);
case String(s): print_string(s);
case _: skip;
}
// this is not legal
switch x {
case Bool(i):
case Int(i): skip; // i cannot be int and bool at the same time
case _: skip;
}
// nor this:
switch x {
case Bool(_):
case Int(i):
case Int3(i): print_int(i); // unbound variable i
case _: skip;
}
// but this *is* legal
switch x {
case Bool(_):
case Int(i):
case Int3(i): skip;
case _: skip;
}