Variants and pattern matching implementation

Reiner Pope reiner at none.com
Sun May 6 22:58:19 PDT 2007


An interesting exercise I've been working on in D has been implementing 
type-safe variants (aka discriminated unions) in D, the main feature of 
which is the ability to do a simple form of pattern matching on them.

It's now at a usable stage. Below is some annotated sample code, showing 
the features, and attached is the sample code in a file, and the 
implementation itself.

Comments would be much appreciated.

Cheers,

Reiner


Variant sample:

import variant;
import std.stdio;

// Setting it up for examples below
void showOverload(int n) { writefln("int"); }
void showOverload(char[] c) { writefln("char[]"); }


void main()
{
//  The base data-structure is the Variant struct, which takes a list of 
types:
     alias Variant!(int, long, char[]) MyVariant; // MyVariant is a type 
which can be assigned ints, char[]s, or Foo's.

//  This data-type has opAssign overloaded, to allow:
     MyVariant v;
     v = 5; // it now holds an int
     v = 5L; // it now holds a long
     v = "World!"; // now holds a char[]

//  The point of interest is that it keeps track of the stored type, and 
pattern matching (similar to a switch statement)
//  can be used to access the data in a type-safe way:
     mixin(Match!(v,
              Case!("int n", `writefln("Twice your number is %d", 2*n);`),
              Case!("char[] s", `writefln("Hello ", s);`)));

//  The Match!() statement ensures that the value is only used as an 
appropriate type.
//  However, it needn't be used with the exact type. Instead (just as a 
proof of concept), if no exact match is found,
//  it follows D's overloading rules (to a certain extent), and it will 
match with any type which can be implicitly
//  converted to the type specified in the pattern, so
     mixin(Match!(v,
              Case!("real r", `writefln("Your value is a long or an int 
with a value of ", r);`)));

//  Also as a neat thing, we can express multiple cases with one 
pattern. The supplied code is then inserted once for each
//  different type specified by the pattern (similar to the quasi-static 
foreach -- that's the main reason I put it in):
     mixin(Match!(v,
              Case!("{int|char[]} n", `showOverload(n);`)));
//  The above code will print "int" if matched with an int, and "char[]" 
if matched with a char[]

//  If a pattern is unreachable, you will be told at compile time:
     mixin(Match!(v,
              Case!("real r", `writefln("Matches int and long");`)
//            , Case!("int n", `writefln("Matches int");`) // This line 
would give an error:
                                                            // 
"Unreachable case statement: int n"
              ));

//  Finally, you can use MatchStrict!() to express a match statement 
with the requirement that every possible type is
//  handled. This is useful if the possible data-types will change, and 
you want to be warned in the future if you don't handle
//  all of them. Other than this requirement, MatchStrict!() behaves 
just like Match!()
     mixin(MatchStrict!(v,
              Case!("{int|char[]} a", `// Do nothing`),
              Case!("long l", `writefln("Aha! A long");`) // Commenting 
this line out would cause an error at compile-time:
                                                          // "Not all 
cases expressed in match statement"
                   ));

}

-------------- next part --------------
A non-text attachment was scrubbed...
Name: variant.d
Type: text/x-dsrc
Size: 8195 bytes
Desc: not available
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20070507/5f6d9f3f/attachment.d>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: variantsample.d
Type: text/x-dsrc
Size: 2857 bytes
Desc: not available
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20070507/5f6d9f3f/attachment-0001.d>


More information about the Digitalmars-d mailing list