Option types and pattern matching.
TheFlyingFiddle via Digitalmars-d
digitalmars-d at puremagic.com
Sun Oct 25 11:15:18 PDT 2015
On Sunday, 25 October 2015 at 14:43:25 UTC, Nerve wrote:
> On Sunday, 25 October 2015 at 06:22:51 UTC, TheFlyingFiddle
> wrote:
> That is actually freaking incredible. It evaluates to a value,
> unwraps values, matches against the None case...I guess the
> only thing it doesn't do is have compiler-enforced matching on
> all cases. Unless I'm just slow this morning and not thinking
> of other features a pattern match should have.
With some changes to the match function one could enforce that a
default handler is always present so that all cases are handled
or error on compilation if it's not.
Something like: (naive way)
auto ref match(Handlers...)(Variant v)
{
//Default handler must be present and be the last handler.
static assert(Parameters!(Handlers[$ - 1]).length == 0,
"Matches must have a default handler.");
}
now
//Would be a compiler error.
v.match!((int n) => n.to!string));
//Would work.
v.match!((int n) => n.to!string),
() => "empty");
Additionally one could check that all return types share a common
implicit conversion type. And cast to that type in the match.
//Returns would be converted to long before being returned.
v.match!((int n) => n, //Returns int
(long n) => n, //Returns long
() => 0);
Or if they don't share a common implicit conversion type return a
Variant result.
Also the handlers could be sorted so that the more general
handlers are tested later.
//Currently
v.match!((int n) => n,
(CMatch!7) => 0,
() => 0);
Would not really work since (int n) is tested for first so
CMatch!7 would never get called even if the value was 7. But if
we sort the incoming Handlers with CMatch instances at the front
then the above would work as a user intended. This would also
allow the empty/default case to be in any order.
For even more error checking one could make sure that no CMatch
value intersects with another. That way if there are for example
two cases with CMatch!7 then an assert error would be emited.
So:
v.match!((CMatch!7) => "The value 7",
(CMatch!7) => "A seven value",
() => "empty");
Would error with something like "duplicate value in match"
Other extensions one could do to the pattern matching is:
1. Allow more then one value in CMatch. So CMatch!(5, 7) would
mean either 5 or 7.
2. Rust has a range syntax, this could be kind of nice. Maybe
RMatch!(1, 10) for that.
3. Add a predicate match that takes a lambda.
//Predicate match.
struct PMatch(alias lambda)
{
alias T = Parameters!(lambda)[0];
alias this value;
T value;
static bool match(Variant v)
{
alias P = Parameters!lambda;
if(auto p = v.peek!P)
{
if(lambda(*p))
{
value = *p;
return true;
}
}
return false;
}
}
struct RMatch(T...) if(T.length == 2)
{
alias C = CommonType!(typeof(T[0]), typeof(T[1]));
C value;
alias this value;
static bool match(Variant v)
{
if(auto p = v.peek!C)
{
if(*p >= T[0] && *p < T[1])
{
value = *p;
return true;
}
}
return false;
}
}
v.match!(
(RMatch!(1, 10) n) => "Was (1 .. 10): " ~ n.to!string;
(PMatch!((int x) => x % 2 == 0) n) => "Was even: " ~
n.to!string,
(PMatch!((int x) => x % 2 == 1) n) => "Was odd: " ~
n.to!string,
() => "not an integer");
The PMatch syntax is not the most fun... It can be reduced
slightly if your not using a variant but a Maybe!T type or a
regular old type to.
The pattern matching can have more static checks and the syntax
can look a somewhat better if we are matching on a Maybe!T type
or a regular type instead of a variant. We could for example make
sure that all CMatch/RMatch values have the correct type and (in
some limited cases) ensure that all cases are covered without the
need for a default switch.
All in all I think that something like this would be a fairly
comprehensive library pattern matching solution. Catching many
types of programming errors at compile-time. It could be fast as
well if all the constants and ranges are converted into a switch
statements (via string mixin magic).
This problem has gained my interest and I plan on implementing
this sometime this week. I'll post a link to the source when it's
done if anyone is interested in it.
More information about the Digitalmars-d
mailing list