Pattern matching as a library

Simen Kjaeraas via Digitalmars-d digitalmars-d at puremagic.com
Sat Mar 12 18:33:49 PST 2016


On Saturday, 12 March 2016 at 20:56:47 UTC, Jacob Carlborg wrote:
> On 12/03/16 14:12, Simen Kjaeraas wrote:
>> As I once again bemoaned D's lack of pattern matching 
>> yesterday, I was
>> inspired to create this[0] implementation, that plays to D's 
>> strengths,
>> allows for user-defined matching, and has a fairly usable 
>> syntax. The
>> core usage looks like this:
>>
>> unittest {
>>    auto a = tuple(1, "foo");
>>    auto b = match(a) (
>>      _!(int, "foo") = (int i) => 1,
>>      _!(_, _)       = ()      => 0
>>    );
>>    assert(b == 1);
>> }
>
> What kind of syntax is that? Is "match" returning a struct with 
> opCall that is called immediately?

Indeed. The goal was to make it look similar to a switch 
statement. I actually started out with an idea for expanding 
switch with pattern matching using lowerings, then noticed I 
could do most of the stuff I wanted without compiler changes.


>> With the user-defined matching implemented as follows:
>>
>> struct Tuple(T...) {
>>     // Implementation
>>
>>    // Magic happens here
>>    bool opMatch(Pattern, Args...)(Pattern p, ref Args args) {
>>      foreach (i, e; p.pattern) {
>>        static if (isTypeTuple!e) {
>>          enum n = countTypes!(p.pattern[0..i]);
>>          args[n] = fields[i];
>>        } else static if (!ignore!e) {
>>          if (fields[i] != e) {
>>            return false;
>>          }
>>        }
>>      }
>>    }
>> }
>
> Is the tuple iterating all patterns to see if there's a match? 
> Shouldn't that be the job for the the match function?

The match function goes through the list of patterns and for each 
one asks the tuple if opMatch returns true for that pattern. If 
it does, the function assigned that pattern is called with the 
values assigned to args.

opMatch here is checking for each element of the pattern if it 
matches the corresponding element of the tuple. Since the pattern 
is available at compile-time, opMatch can deny patterns it 
doesn't like (e.g. trying to match a Tuple!(int, string) with a 
string).

The match function is really only a framework for having similar 
matching syntax for dissimilar types.

If the capability of matching patterns to types were in the match 
function, how could a user type override it? Matching on a 
Tuple!(string, string) is different from matching on an 
Algebraic!(int[], Foo*) is different from matching on a 
specialized user type that wants to do something real weird (I'm 
not sure what that'd be, but I'm sure there are people who will 
want to).


> I've started implementing a pattern matching function as well. 
> It has a syntax that only use compile time parameters, because 
> both types and values can be passed. I'm not entirely sure on 
> the syntax yet. I'll have to see what's possible to implement. 
> Some suggestions:
>
> auto a = tuple(1, "foo");
> auto b = match!(a,
>     int, "foo", (int i) => 1,
>     _, _, () => 0,
> );

That works. I feel the grouping is looser than in my example, and 
that the pattern doesn't stand out from the rest of the 
expression, but it certainly works, and there are some benefits 
to that syntax.


> If the pull request for inspecting templates ever will be 
> merged it won't be necessary to have typed lambdas:
>
> auto b = match!(a,
>     int, "foo", (i) => 1,
>     _, _, () => 0,
> );

There's a problem using that syntax? It works for me in a toy 
example:

http://dpaste.dzfl.pl/7360ee90b344

Sorry about the lack of comments and stuff, but it's 3:30AM, and 
I probably shouldn't be programming now.


More information about the Digitalmars-d mailing list