Pattern matching example

Philippe Sigaud philippe.sigaud at gmail.com
Sun Apr 4 13:07:25 PDT 2010


On Sun, Apr 4, 2010 at 17:18, Lutger <lutger.blijdestijn at gmail.com> wrote:

> bearophile wrote:
>
> > I'm not currently asking to implement pattern matching in D3 (as present
> > for example in Scala), but it's positive to think how to solve similar
> > problems in D2, because even if pattern matching is not available in D2,
> > the problems it is asked to solve solve can be real.
> >
> > Walter or Andrei has recently shown here a possible implementation idea,
> > but I don't remember where the post is and what the implementation was.
> And
> > it wasn't a small compilable program.
>
> It was a subthread under the 'is D a cult' thread, this was the basic
> example:
>
> foo(  v,
>      (int i) { writeln("I saw an int ", i); },
>      (string s) { writeln("I saw a string ", s); ),
>      (Variant any) { writeln("I saw the default case ", any); }
>    );
>
>
I went the other way round and wrote something more compile-time oriented:

usage:

match!(fun1, fun2, fun3)(someValues...); // as many values as you wish

where the funX can be template functions (or anything template-y _and_
callable), to harness the pattern matching inherent in templates, variadic
templates, and so on.

For example, given:

string any(T...)(T t) { return "any: " ~ typeof(t).stringof;}

T one(T)(T t) { return t;}

T[] twoEqual(T)(T t1, T t2) { return [t1,t2];}

Tuple!(T,U) twoDiff(T,U)(T t, U u) { return tuple(t,u);}

string three(T)(T t1, T t2, T t3) { return T.stringof ~ " thrice";}

you can do:

alias match!(one, twoEqual, twoDiff, three, any) matcher;

auto m = matcher(1);     // 1
auto m = matcher(1,2); // [1,2]
auto m = matcher(1,'a'); // Tuple!(int,char)(1,'a')
auto m = matcher(1,2,3,4); // string "any: (int,int,int,int)"

You can also use standard functions, of course, and mix templates and
functions.
I used this to create trees of tuples (tuples of values and tuples) and
pattern-match on them. It's far from being as beautiful as Haskell code, but
it's enough for my needs.

One interest compared to function overload is that you can have different
return types depending on the function you give it as params.

I have another version that deconstruct the values: if you send it a struct
containing two ints and one of your function has two ints as params, it will
match. But it's buggy as hell and I sould have a look at it before posting
it.

For match, here is the code:

template match(alias fun, Rest...) {
    auto match(M...)(M m) { // curried template...
            return Matcher!(fun, M.length, M, Rest)(m)();
    }
}

struct Matcher(alias fun, size_t n, Rest...) {
    TypeTuple!(Rest[0..n]) _m;
    this(TypeTuple!(Rest[0..n]) m) { _m = m;}
    auto opCall() {
        static if (is(typeof(fun(_m))))
            return fun(_m);
        else static if (n == 1 && is(typeof(fun(_m[0])))) // I'm not sure
this is necessary
            return fun(_m[0]);
        else static if (Rest.length > n) {
            return Matcher!(Rest[n], n, Rest[0..n], Rest[n+1..$])(_m)();
        }
        else { // no match found, we are at the end of Rest
            string s;
            foreach(i, Type; TypeTuple!(Rest[0..n])) s ~= to!string(_m[i]);
            throw new NoMatchException("No match for " ~ s ~ " of type " ~
typeof(_m).stringof);
        }
    }
}

Half the complexity and ugliness comes from my wanting to have a variadic
function in the end: I did_not_ want have to wrap my arguments in a tuple,
as it's done for std.concurrency, IIRC.
Also, I use functions like (...) { some code } or (T...)(T t) { } to get a
function that matches everything, not a (Variant v) { }

Philippe
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20100404/423ac039/attachment.html>


More information about the Digitalmars-d mailing list