On Sun, Apr 4, 2010 at 17:18, Lutger <lutger.blijdestijn@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