Option types and pattern matching.

Edmund Smith via Digitalmars-d digitalmars-d at puremagic.com
Mon Oct 26 04:40:07 PDT 2015


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).


More information about the Digitalmars-d mailing list