On Sunday, 25 October 2015 at 06:22:51 UTC, TheFlyingFiddle wrote:
On Sunday, 25 October 2015 at 05:45:15 UTC, Nerve wrote:
On Sunday, 25 October 2015 at 05:05:47 UTC, Rikki Cattermole wrote:
Since I have no idea what the difference between Some(_), None and default. I'll assume it's already doable.

_ represents all existing values not matched. In this case, Some(_) represents any integer value that is not 7. None specifically matches the case where no value has been returned. We are, in most languages, also able to unwrap the value:

match x {
    Some(7) => "Lucky number 7!",
    Some(n) => "Not a lucky number: " ~ n,
    None => "No value found"
}

You can do something very similar to that. With slightly different syntax.

import std.traits;
import std.conv;
import std.variant;
struct CMatch(T...) if(T.length == 1)
{
   alias U = typeof(T[0]);
   static bool match(Variant v)
   {
      if(auto p = v.peek!U)
         return *p == T[0];
      return false;
   }
}

auto ref match(Handlers...)(Variant v)
{
   foreach(handler; Handlers)
   {
      alias P = Parameters!handler;
      static if(P.length == 1)
      {
         static if(isInstanceOf!(CMatch, P[0]))
         {
            if(P[0].match(v))
               return handler(P[0].init);
         }
         else
         {
            if(auto p = v.peek!(P[0]))
               return handler(*p);
         }
      }
      else
      {
         return handler();
      }
   }

   assert(false, "No matching pattern");
}

unittest
{
    Variant v = 5;
    string s = v.match!(
        (CMatch!7) => "Lucky number seven",
        (int n)    => "Not a lucky number: " ~ n.to!string,
        ()         => "No value found!");

   writeln(s);
}

You could also emulate constant matching using default parameters (albeit with the restriction that they must be after any non-default/constant parameters), since the defaults form part of the function's type. I tried making something like this earlier this summer and it'd check that a given value was first equal to the default parameter and match if so, or match if there was no default parameter but the types matched.

e.g.
//template ma(tch/g)ic

unittest
{
    Algebraic!(string, int, double, MyStruct) v = 5;
    string s = v.match!(
        (string s = "") => "Empty string!",
        (string s) => s,
        (int i = 7) => "Lucky number 7",
        (int i = 0) => "Nil",
        (int i) => i.to!string,
        (double d) => d.to!string,
        (MyStruct m = MyStruct(15)) => "Special MyStruct value",
        (MyStruct m) => m.name, //
        () => "ooer");
    writeln(s);
}

It's a bit ugly overloading language features like this, but it makes the syntax a little prettier.

I'd really like to see proper pattern matching as a language-level feature however; for all the emulating it we can do in D, it's not very pretty or friendly and optimising it is harder since the language has no concept of pattern matching. Things like Option (and other ADTs) are lovely, but really need good pattern matching to become worthwhile IMO (e.g. Java Optional<T> has a get() method that throws on empty, which undermines the main reason to use optional - to have a guarantee that you handle the empty case gracefully; Scala's Option is really nice on the other hand since you can/should pattern match).

Reply via email to