View source code
Display the source code in std/sumtype.d from which this page was generated on github.
Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using local clone.

Template std.sumtype.match

Calls a type-appropriate function with the value held in a [SumType].

template match(handlers...) ;

For each possible type the [SumType] can hold, the given handlers are checked, in order, to see whether they accept a single argument of that type. The first one that does is chosen as the match for that type. (Note that the first match may not always be the most exact match. See ["Avoiding unintentional matches"](#avoiding-unintentional-matches) for one common pitfall.)

Every type must have a matching handler, and every handler must match at least one type. This is enforced at compile time.

Handlers may be functions, delegates, or objects with opCall overloads. If a function with more than one overload is given as a handler, all of the overloads are considered as potential matches.

Templated handlers are also accepted, and will match any type for which they can be [implicitly instantiated](https://dlang.org/glossary.html#ifti). See ["Introspection-based matching"](#introspection-based-matching) for an example of templated handler usage.

If multiple [SumType]s are passed to match, their values are passed to the handlers as separate arguments, and matching is done for each possible combination of value types. See ["Multiple dispatch"](#multiple-dispatch) for an example.

Contained Functions

NameDescription
match The actual match function.

Returns

The value returned from the handler that matches the currently-held type.

See Also

visit

Example

Avoiding unintentional matches

Sometimes, implicit conversions may cause a handler to match more types than intended. The example below shows two solutions to this problem.

alias Number = SumType!(double, int);

Number x;

// Problem: because int implicitly converts to double, the double
// handler is used for both types, and the int handler never matches.
assert(!__traits(compiles,
    x.match!(
        (double d) => "got double",
        (int n) => "got int"
    )
));

// Solution 1: put the handler for the "more specialized" type (in this
// case, int) before the handler for the type it converts to.
assert(__traits(compiles,
    x.match!(
        (int n) => "got int",
        (double d) => "got double"
    )
));

// Solution 2: use a template that only accepts the exact type it's
// supposed to match, instead of any type that implicitly converts to it.
alias exactly(T, alias fun) = function (arg)
{
    static assert(is(typeof(arg) == T));
    return fun(arg);
};

// Now, even if we put the double handler first, it will only be used for
// doubles, not ints.
assert(__traits(compiles,
    x.match!(
        exactly!(double, d => "got double"),
        exactly!(int, n => "got int")
    )
));

Example

Multiple dispatch

Pattern matching can be performed on multiple SumTypes at once by passing handlers with multiple arguments. This usually leads to more concise code than using nested calls to match, as show below.

struct Point2D { double x, y; }
struct Point3D { double x, y, z; }

alias Point = SumType!(Point2D, Point3D);

version (none)
{
    // This function works, but the code is ugly and repetitive.
    // It uses three separate calls to match!
    @safe pure nothrow @nogc
    bool sameDimensions(Point p1, Point p2)
    {
        return p1.match!(
            (Point2D _) => p2.match!(
                (Point2D _) => true,
                _ => false
            ),
            (Point3D _) => p2.match!(
                (Point3D _) => true,
                _ => false
            )
        );
    }
}

// This version is much nicer.
@safe pure nothrow @nogc
bool sameDimensions(Point p1, Point p2)
{
    alias doMatch = match!(
        (Point2D _1, Point2D _2) => true,
        (Point3D _1, Point3D _2) => true,
        (_1, _2) => false
    );

    return doMatch(p1, p2);
}

Point a = Point2D(1, 2);
Point b = Point2D(3, 4);
Point c = Point3D(5, 6, 7);
Point d = Point3D(8, 9, 0);

assert( sameDimensions(a, b));
assert( sameDimensions(c, d));
assert(!sameDimensions(a, c));
assert(!sameDimensions(d, b));

Authors

Paul Backus

License

Boost License 1.0