Report a bug
If you spot a problem with this page, click here to create a GitHub 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 a local clone.

mir.algebraic

Variant and Nullable types

This module implements a discriminated union type (a.k.a. tagged union, algebraic type). Such types are useful for type-uniform binary interfaces, interfacing with scripting languages, and comfortable exploratory programming.
The module defines generic Algebraic type that contains a payload. The allowed types of the paylad are defined by the unordered TypeSet.
Algebraic template accepts two arguments: self type set id and a list of type sets.

Algebraic Aliases

Name Description
Variant an algebraic type
TaggedVariant a tagged algebraic type
Nullable an algebraic type with at least typeof(null)

Visitor Handlers

Name Ensures can match Throws if no match Returns Nullable Multiple dispatch Argumments count Fuses Algebraic types on return
    Classic handlers
visit Yes N/A No No 1+ No visit, Yes, N/A, No, No, 1+, No
optionalVisit No No Yes No 1+ No optionalVisit, No, No, Yes, No, 1+, No
autoVisit No No auto No 1+ No autoVisit, No, No, auto, No, 1+, No
tryVisit No Yes No No 1+ No tryVisit, No, Yes, No, No, 1+, No
    Multiple dispatch and algebraic fusion on return
match Yes N/A No Yes 0+ Yes match, Yes, N/A, No, Yes, 0+, Yes
optionalMatch No No Yes Yes 0+ Yes optionalMatch, No, No, Yes, Yes, 0+, Yes
autoMatch No No auto Yes 0+ Yes autoMatch, No, No, auto, Yes, 0+, Yes
tryMatch No Yes No Yes 0+ Yes tryMatch, No, Yes, No, Yes, 0+, Yes
     Inner handlers. Multiple dispatch and algebraic fusion on return.
suit N/A(Yes) N/A No Yes ? Yes suit, N/A(Yes), N/A, No, Yes, ?, Yes
some N/A(Yes) N/A No Yes 0+ Yes some, N/A(Yes), N/A, No, Yes, 0+, Yes
none N/A(Yes) N/A No Yes 1+ Yes none, N/A(Yes), N/A, No, Yes, 1+, Yes
assumeOk Yes(No) No(Yes) No(Yes) Yes(No) 0+ Yes(No) assumeOk, Yes(No), No(Yes), No(Yes), Yes(No), 0+, Yes(No)
    Member access
getMember Yes N/A No No 1+ No getMember, Yes, N/A, No, No, 1+, No
optionalGetMember No No Yes No 1+ No optionalGetMember, No, No, Yes, No, 1+, No
autoGetMember No No auto No 1+ No autoGetMember, No, No, auto, No, 1+, No
tryGetMember No Yes No No 1+ No tryGetMember, No, Yes, No, No, 1+, No
    Member access with algebraic fusion on return
matchMember Yes N/A No No 1+ Yes matchMember, Yes, N/A, No, No, 1+, Yes
optionalMatchMember No No Yes No 1+ Yes optionalMatchMember, No, No, Yes, No, 1+, Yes
autoMatchMember No No auto No 1+ Yes autoMatchMember, No, No, auto, No, 1+, Yes
tryMatchMember No Yes No No 1+ Yes tryMatchMember, No, Yes, No, No, 1+, Yes

Special Types

Name Description
void It is usefull to indicate a possible return type of the visitor. Can't be accesed by reference.
typeof(null) It is usefull for nullable types. Also, it is used to indicate that a visitor can't match the current value of the algebraic. Can't be accesed by reference.
This Dummy structure that is used to construct self-referencing algebraic types. Example: Variant!(int, double, string, This*[2])
SetAlias!setId Dummy structure that is used to construct cyclic-referencing lists of algebraic types.
TaggedType Dummy type used to associate tags with type.
Err Wrapper to denote an error value type.
reflectErr Attribute that denotes that the type is an error value type.

Algebraic Traits

Name Description
isVariant Checks if the type is instance of Algebraic.
isNullable Checks if the type is instance of Algebraic with a self TypeSet that contains typeof(null).
isTaggedVariant Checks if the type is instance of tagged Algebraic.
isTypeSet Checks if the types are the same as TypeSet of them.
ValueTypeOfNullable Gets type of
  • .Algebraic.get.2
  • method.
    SomeVariant Gets subtype of algebraic without types for which isErr is true.
    NoneVariant Gets subtype of algebraic with types for which isErr is true.
    isErr Checks if T is a instance of Err or if it is annotated with reflectErr.
    isResultVariant Checks if T is a Variant with at least one allowed type that satisfy isErr traits.

    Type Set

    • TaggedTypeSet is supported. Example:TargetTypeSet!(["integer", "floating"], int, double)
    • .
    • Type set is unordered. Example:TypeSet!(int, double) and TypeSet!(double, int) are the same.
    • Duplicats are ignored. Example: TypeSet!(float, int, float) and TypeSet!(int, float) are the same.
    • Types are automatically unqualified if this operation can be performed implicitly. Example: TypeSet!(const int) and TypeSet!int` are the same.
    • Non trivial TypeSet!(A, B, ..., etc) is allowed.
    • Trivial TypeSet!T is allowed.
    • Empty TypeSet!() is allowed.

    Visitors

    • Visitors are allowed to return values of different types If there are more then one return type then the an Algebraic type is returned.
    • Visitors are allowed to accept additional arguments. The arguments can be passed to the visitor handler.
    • Multiple visitors can be passes to the visitor handler.
    • Visitors are matched according to the common Dlang Function Overloading rules.
    • Visitors are allowed accept algebraic value by reference except the value of typeof(null).
    • Visitors are called without algebraic value if its algebraic type is void.
    • If the visitors arguments has known types, then such visitors should be passed to a visitor handler before others to make the compiler happy. This includes visitors with no arguments, which is used to match void type.

    Implementation Features

    • BetterC support. Runtime TypeInfo is not used.
    • Copy-constructors and postblit constructors are supported.
    • toHash, opCmp. opEquals, and toString support.
    • No string or template mixins are used.
    • Optimised for fast execution.
    • some / none idiom.
    See Also:
    License:
    Authors:
    Ilya Yaroshenko
    enum bool isVariant(T);
    Checks if the type is instance of Algebraic.
    Examples:
    static assert(isVariant!(Variant!(int, string)));
    static assert(isVariant!(const Variant!(int[], string)));
    static assert(isVariant!(Nullable!(int, string)));
    static assert(!isVariant!int);
    
    enum bool isTaggedVariant(T);
    Checks if the type is instance of tagged Algebraic.
    Tagged algebraics can be defined with TaggedVariant.
    Examples:
    static assert(!isTaggedVariant!int);
    static assert(!isTaggedVariant!(Variant!(int, string)));
    static assert(isTaggedVariant!(TaggedVariant!(["integer", "string"], int, string)));
    
    enum bool isNullable(T);
    Checks if the type is instance of Algebraic with a self TypeSet that contains typeof(null).
    Examples:
    static assert(isNullable!(const Nullable!(int, string)));
    static assert(isNullable!(Nullable!()));
    
    static assert(!isNullable!(Variant!()));
    static assert(!isNullable!(Variant!string));
    static assert(!isNullable!int);
    static assert(!isNullable!string);
    
    template ValueTypeOfNullable(T : Algebraic!(typeof(null), Types), Types...)
    Gets type of
  • .Algebraic.get.2
  • method.
    Examples:
    static assert(is(ValueTypeOfNullable!(const Nullable!(int, string)) == Algebraic!(int, string)));
    static assert(is(ValueTypeOfNullable!(Nullable!()) == Algebraic!()));
    static assert(is(typeof(Nullable!().get()) == Algebraic!()));
    
    struct This;
    Dummy type for Variant and Nullable self-referencing.
    struct TaggedType(T, string name) if (name.length);
    Dummy type used to associate tags with a type.
    enum auto isTaggedType(T);
    Checks if T is TaggedType instance.
    template getTaggedTypeUnderlying(T : TaggedType!(I, name), I, string name)
    Gets TaggedType underlying type.
    enum auto getTaggedTypeName(T : TaggedType!(I, name), I, string name);
    Gets TaggedType tag name.
    Examples:

    Self-Referential Types

    A useful and popular use of algebraic data structures is for defining self-referential data structures, i.e. structures that embed references to values of their own type within. This is achieved with Variant by using This as a placeholder whenever a reference to the type being defined is needed. The Variant instantiation will perform alpha renaming on its constituent types, replacing This with the self-referenced type. The structure of the type involving This may be arbitrarily complex.
    import mir.functional: Tuple = RefTuple;
    
    // A tree is either a leaf or a branch of two others
    alias Tree(Leaf) = Variant!(Leaf, Tuple!(This*, This*));
    alias Leafs = Tuple!(Tree!int*, Tree!int*);
    
    Tree!int tree = Leafs(new Tree!int(41), new Tree!int(43));
    Tree!int* right = tree.get!Leafs[1];
    assert(*right == 43);
    
    Examples:
    // An object is a double, a string, or a hash of objects
    alias Obj = Variant!(double, string, This[string], This[]);
    alias Map = Obj[string];
    
    Obj obj = "hello";
    assert(obj._is!string);
    assert(obj.trustedGet!string == "hello");
    obj = 42.0;
    assert(obj.get!double == 42);
    obj = ["customer": Obj("John"), "paid": Obj(23.95)];
    assert(obj.get!Map["customer"] == "John");
    
    template TypeSet(T...)
    Type set resolution template used to construct Algebraic .
    template TaggedTypeSet(string[] tagNames, T...)
    Type set for tagged Variants self-referencing.
    enum bool isTypeSet(T...);
    Checks if the type list is TypeSet.
    Examples:
    static assert(isTypeSet!(TypeSet!()));
    static assert(isTypeSet!(TypeSet!void));
    static assert(isTypeSet!(TypeSet!(void, int, typeof(null))));
    
    template Variant(T...)
    Variant Type (aka Algebraic Type).
    The impllementation is defined as
    alias Variant(T...) = Algebraic!(TypeSet!T);
    
    Compatible with BetterC mode.
    Examples:
    Variant!(int, double, string) v = 5;
    assert(v.get!int == 5);
    v = 3.14;
    assert(v == 3.14);
    // auto x = v.get!long; // won't compile, type long not allowed
    // v = '1'; // won't compile, type char not allowed
    
    Examples:
    Single argument Variant
    static struct S
    {
        int n;
        this(ref return scope inout S rhs) inout
        {
            this.n = rhs.n + 1;
        }
    }
    
    Variant!S a = S();
    auto b = a;
    
    import mir.conv;
    assert(a.get!S.n == 0);
    assert(b.n == 1); //direct access of a member in case of all algebraic types has this member
    
    Examples:
    Empty type set
    Variant!() a;
    auto b = a;
    assert(a.toHash == 0);
    assert(a == b);
    assert(a <= b && b >= a);
    static assert(typeof(a).sizeof == 1);
    
    Examples:
    Small types
    static struct S { ubyte d; }
    static assert(Nullable!(byte, char, S).sizeof == 2);
    
    template TaggedVariant(string[] tags, T...)

    template TaggedVariant(T) if (is(T == union))
    Tagged Variant Type (aka Tagged Algebraic Type).
    Compatible with BetterC mode.
    Template has two declarations:
    alias TaggedVariant(string[] tags, T...) = Variant!(applyTags!(tags, T));
    // and
    template TaggedVariant(T)
        if (is(T == union))
    {
        ...
    }
    
    Examples:
    Json Value
    static union JsonUnion
    {
        long integer;
        double floating;
        bool boolean;
        typeof(null) null_;
        immutable(char)[] string;
        This[] array;
        This[immutable(char)[]] object;
    }
    
    alias JsonValue = TaggedVariant!JsonUnion;
    
    // typeof(null) has priority
    static assert(JsonValue.Kind.init == JsonValue.Kind.null_);
    static assert(JsonValue.Kind.null_ == 0);
    
    // Kind and AllowedTypes has the same order
    static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.array] == JsonValue[]));
    static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.boolean] == bool));
    static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.floating] == double));
    static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.integer] == long));
    static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.null_] == typeof(null)));
    static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.object] == JsonValue[string]));
    
    JsonValue v;
    assert(v.kind == JsonValue.Kind.null_);
    
    v = 1;
    assert(v.kind == JsonValue.Kind.integer);
    assert(v == 1);
    v = JsonValue(1);
    assert(v == 1);
    v = v.get!(long, double);
    
    v = "Tagged!";
    assert(v.get       !string                  == "Tagged!");
    assert(v.trustedGet!string                  == "Tagged!");
    
    assert(v.kind == JsonValue.Kind.string);
    
    assert(v.get!"string" == "Tagged!"); // string-based get
    assert(v.trustedGet!"string" == "Tagged!"); // string-based trustedGet
    
    assert(v.get!(JsonValue.Kind.string) == "Tagged!"); // Kind-based get
    assert(v.trustedGet!(JsonValue.Kind.string) == "Tagged!"); // Kind-based trustedGet
    
    v = [JsonValue("str"), JsonValue(4.3)];
    
    assert(v.kind == JsonValue.Kind.array);
    assert(v.trustedGet!(JsonValue[])[1].kind == JsonValue.Kind.floating);
    
    v = null;
    assert(v.kind == JsonValue.Kind.null_);
    
    Examples:
    Wrapped algebraic with propogated primitives
    static struct Response
    {
        alias Union = TaggedVariant!(
            ["double_", "string", "array", "table"],
            double,
            string,
            Response[],
            Response[string],
        );
    
        Union data;
        alias Tag = Union.Kind;
        // propogates opEquals, opAssign, and other primitives
        alias data this;
    
        static foreach (T; Union.AllowedTypes)
            this(T v) @safe pure nothrow @nogc { data = v; }
    }
    
    Response v = 3.0;
    assert(v.kind == Response.Tag.double_);
    v = "str";
    assert(v == "str");
    
    template Nullable(T...)

    Nullable!T nullable(T)(T t);
    Nullable Variant Type (aka Algebraic Type).
    The impllementation is defined as
    alias Nullable(T...) = Variant!(typeof(null), T);
    
    In additional to common algebraic API the following members can be accesssed:
    Compatible with BetterC mode.
    Examples:
    Single type Nullable
    static assert(is(Nullable!int == Variant!(typeof(null), int)));
    
    Nullable!int a = 5;
    assert(a.get!int == 5);
    
    a.nullify;
    assert(a.isNull);
    
    a = 4;
    assert(!a.isNull);
    assert(a.get == 4);
    assert(a == 4);
    a = 4;
    
    a = null;
    assert(a == null);
    
    Examples:
    Empty nullable type set support
    Nullable!() a;
    auto b = a;
    assert(a.toHash == 0);
    assert(a == b);
    assert(a <= b && b >= a);
    static assert(typeof(a).sizeof == 1);
    
    struct Algebraic(_Types...);
    Algebraic implementation. For more portable code, it is higly recommeded to don't use this template directly. Instead, please use of Variant and Nullable, which sort types.
    Examples:
    Constructor and methods propagation.
    static struct Base
    {
        double d;
    }
    
    static class C
    {
        // alias this members are supported 
        Base base;
        alias base this;
    
        int a;
        private string _b;
    
    @safe pure nothrow @nogc:
    
        string b() const @property { return _b; }
        void b(string b) @property { _b = b; }
    
        int retArg(int v) { return v; }
    
        this(int a, string b)
        {
            this.a = a;
            this._b = b;
        }
    }
    
    static struct S
    {
        string b;
        int a;
    
        double retArg(double v) { return v; }
    
        // alias this members are supported 
        Base base;
        alias base this;
    }
    
    static void inc(ref int a) { a++; }
    
    alias V = Nullable!(C, S); // or Variant!
    
    auto v = V(2, "str");
    assert(v._is!C);
    assert(v.a == 2);
    assert(v.b == "str");
    // members are returned by reference if possible
    inc(v.a);
    assert(v.a == 3);
    v.b = "s";
    assert(v.b == "s");
    // alias this members are supported 
    v.d = 10;
    assert(v.d == 10);
    // method call support
    assert(v.retArg(100)._is!int);
    assert(v.retArg(100) == 100);
    
    v = V("S", 5);
    assert(v._is!S);
    assert(v.a == 5);
    assert(v.b == "S");
    // members are returned by reference if possible
    inc(v.a);
    assert(v.a == 6);
    v.b = "s";
    assert(v.b == "s");
    // alias this members are supported 
    v.d = 15;
    assert(v.d == 15);
    // method call support
    assert(v.retArg(300)._is!double);
    assert(v.retArg(300) == 300.0);
    
    alias AllowedTypes = AliasSeq!(ReplaceTypeUnless!(isVariant, This, Algebraic!_Types, _UntaggedThisTypeSetList));
    Allowed types list
    See Also:
    Examples:
    import std.meta: AliasSeq;
    
    alias V = Nullable!
    (
        This*,
        string,
        double,
        bool,
    );
    
    static assert(is(V.AllowedTypes == TypeSet!(
        typeof(null),
        bool,
        string,
        double,
        V*)));
    
    enum Kind;
    Algebraic Kind.
    Defined as enum for tagged algebraics and as unsigned for common algebraics.
    The Kind enum contains the members defined using tag names.
    If the algebraic type is Nullable then the default Kind enum member has zero value and corresponds to typeof(null).
    See Also:
    const pure nothrow @nogc @property @safe Kind kind();
    Returns:
    .Algebraic.Kind.
    Defined as enum for tagged algebraics and as unsigned for common algebraics.
    See Also:
    const scope @safe size_t[2] opSlice(size_t dimension)(size_t i, size_t j)
    if (dimension == 0);
    Returns:
    slice type of Slice!(IotaIterator!size_t)
    this(RhsTypes...)(Algebraic!RhsTypes rhs)
    if (allSatisfy!(Contains!AllowedTypes, Algebraic!RhsTypes.AllowedTypes));
    Construct an algebraic type from its subset.
    Examples:
    alias Float = Variant!(float, double);
    alias Int = Variant!(long, int);
    alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes);
    
    Float fp = 3.0;
    Number number = fp; // constructor call
    assert(number == 3.0);
    
    Int integer = 12L;
    number = Number(integer);
    assert(number == 12L);
    
    const nothrow @trusted size_t toHash();
    const @trusted bool opEquals()(auto const ref typeof(this) rhs);
    const @trusted auto opCmp()(auto const ref typeof(this) rhs);
    const string toString()();

    const void toString(W)(ref scope W w);
    Requires mir-algorithm package
    const bool opCast(C)()
    if (is(C == bool));
    const Algebraic opCast(C)()
    if (is(C == Algebraic));
    const @property bool isNull();

    void nullify();

    auto get()()
    if (allSatisfy!(isCopyable, AllowedTypes[1 .. $]) && (AllowedTypes.length != 2));
    Defined if the first type is typeof(null)
    inout ref inout(AllowedTypes[1]) get() return;

    inout @property ref inout(AllowedTypes[1]) get()(auto ref inout(AllowedTypes[1]) fallback) return;
    Gets the value if not null. If this is in the null state, and the optional parameter fallback was provided, it will be returned. Without fallback, calling get with a null state is invalid.
    When the fallback type is different from the Nullable type, get(T) returns the common type.
    Parameters:
    inout(AllowedTypes[1]) fallback the value to return in case the Nullable is null.
    Returns:
    The value held internally by this Nullable.
    Examples:
    enum E { a = "a", b = "b" }
    Nullable!E f = E.a;
    auto e = f.get();
    static assert(is(typeof(e) == E), Nullable!E.AllowedTypes.stringof);
    assert(e == E.a);
    
    assert(f.get(E.b) == E.a);
    
    f = null;
    assert(f.get(E.b) == E.b);
    
    const pure nothrow @nogc @property @safe bool _is(R : Algebraic!RetTypes, RetTypes...)()
    if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes));

    const pure nothrow @nogc @property @safe bool _is(RetTypes...)()
    if (RetTypes.length > 1);
    Checks if the underlaying type is an element of a user provided type set.
    @property ref auto trustedGet(R : Algebraic!RetTypes, this This, RetTypes...)() return
    if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes));

    template trustedGet(RetTypes...) if (RetTypes.length > 1)
    nothrow .Algebraic.get alternative that returns an algebraic subset.
    Examples:
    alias Float = Variant!(float, double);
    alias Int = Variant!(long, int);
    alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes);
    
    Number number = 3.0;
    assert(number._is!Float);
    auto fp = number.trustedGet!Float;
    static assert(is(typeof(fp) == Float));
    assert(fp == 3.0);
    
    // type list overload
    number = 12L;
    assert(number._is!(int, long));
    auto integer = number.trustedGet!(int, long);
    static assert(is(typeof(integer) == Int));
    assert(integer == 12L);
    
    template trustedGet(Kind kind)

    template trustedGet(string kind)
    trustedGet overload that accept .Algebraic.Kind.
    @property ref auto get(R : Algebraic!RetTypes, this This, RetTypes...)() return
    if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes));

    template get(RetTypes...) if (RetTypes.length > 1)
    Gets an algebraic subset.
    Throws:
    Exception if the storage contains value of the type that isn't represented in the allowed type set of the requested algebraic.
    Examples:
    alias Float = Variant!(float, double);
    alias Int = Variant!(long, int);
    alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes);
    
    Number number = 3.0;
    auto fp = number.get!Float;
    static assert(is(typeof(fp) == Float));
    assert(fp == 3.0);
    
    // type list overload
    number = 12L;
    auto integer = number.get!(int, long);
    static assert(is(typeof(integer) == Int));
    assert(integer == 12L);
    
    template get(Kind kind)

    template get(string kind)
    get overload that accept .Algebraic.Kind.
    ref @trusted auto opAssign(RhsTypes...)(Algebraic!RhsTypes rhs) return
    if (RhsTypes.length < AllowedTypes.length && allSatisfy!(Contains!AllowedTypes, Algebraic!RhsTypes.AllowedTypes));
    const pure nothrow @nogc @property @safe bool isOk();
    Determines if the variant holds value of some none-isVariant type. The property is avaliable only for
    template visit(visitors...)
    Applies a delegate or function to the given Variant depending on the held type, ensuring that all types are handled by the visiting functions.
    Examples:
    alias Number = Variant!(int, double);
    
    Number x = 23;
    Number y = 1.0;
    
    assert(x.visit!((int v) => true, (float v) => false));
    assert(y.visit!((int v) => false, (float v) => true));
    
    Examples:
    alias Number = Nullable!(int, double);
    
    Number z = null; // default
    Number x = 23;
    Number y = 1.0;
    
    () nothrow {
        assert(x.visit!((int v) => true, (float v) => false));
        assert(y.visit!((int v) => false, (v) => true));
        assert(z.visit!((typeof(null) v) => true, (v) => false));
    } ();
    
    auto xx = x.get;
    static assert (is(typeof(xx) == Variant!(int, double)));
    assert(xx.visit!((int v) => v, (float v) => 0) == 23);
    assert(xx.visit!((ref v) => v) == 23);
    
    x = null;
    y.nullify;
    
    assert(x.isNull);
    assert(y.isNull);
    assert(z.isNull);
    assert(z == y);
    
    Examples:
    Array primitives propagation
    Variant!(long[], double[]) array;
    array = new long[3];
    array[2] = 100;
    assert(array == [0L, 0, 100]);
    assert(array.length == 3);
    assert(array[2] == 100);
    array.length = 4;
    assert(array == [0L, 0, 100, 0]);
    array = array[2 .. 3];    
    assert(array.length == 1);
    assert(array[0] == 100);
    array[0] = 10.Variant!(long, double);
    assert(array[0] == 10);
    
    Examples:
    Checks .Algebraic.toString and void Algerbraic.toString requries mir-algorithm package
    import mir.conv: to;
    enum MIR_ALGORITHM = __traits(compiles, { import mir.format; });
    
    alias visitorHandler = visit!(
        (typeof(null)) => "NULL",
        () => "VOID",
        (ref r) {r += 1;}, // returns void
    );
    
    alias secondOrderVisitorHandler = visit!(
        () => "SO VOID", // void => to "RV VOID"
        (str) => str, // string to => it self
    );
    
    alias V = Nullable!(void, int);
    static assert(is(V == Variant!(typeof(null), void, int)));
    
    V variant;
    
    assert(secondOrderVisitorHandler(visitorHandler(variant)) == "NULL");
    assert(variant.to!string == "null");
    
    variant = V._void;
    assert(variant._is!void);
    assert(is(typeof(variant.get!void()) == void));
    
    assert(secondOrderVisitorHandler(visitorHandler(variant)) == "VOID");
    assert(variant.to!string == "void");
    
    variant = 5;
    
    assert(secondOrderVisitorHandler(visitorHandler(variant)) == "SO VOID");
    assert(variant == 6);
    assert(variant.to!string == (MIR_ALGORITHM ? "6" : "int"));
    
    template tryVisit(visitors...)
    Behaves as visit but doesn't enforce at compile time that all types can be handled by the visiting functions.
    Throws:
    Exception if naryFun!visitors can't be called with provided arguments
    Examples:
    alias Number = Variant!(int, double);
    
    Number x = 23;
    
    assert(x.tryVisit!((int v) => true));
    
    template optionalVisit(visitors...)
    Behaves as visit but doesn't enforce at compile time that all types can be handled by the visiting functions.
    Returns:
    nullable variant, null value is used if naryFun!visitors can't be called with provided arguments.
    Examples:
    static struct S { int a; }
    
    Variant!(S, double) variant;
    
    alias optionalVisitInst = optionalVisit!((ref value) => value + 0);
    
    // do nothing because of variant isn't initialized
    Nullable!double result = optionalVisitInst(variant);
    assert(result.isNull);
    
    variant = S(2);
    // do nothing because of lambda can't compile
    result = optionalVisitInst(variant);
    assert(result == null);
    
    variant = 3.0;
    result = optionalVisitInst(variant);
    assert (result == 3.0);
    
    template autoVisit(visitors...)
    Behaves as visit but doesn't enforce at compile time that all types can be handled by the visiting functions.
    Returns:
    optionally nullable type, null value is used if naryFun!visitors can't be called with provided arguments.
    template match(visitors...)
    Applies a delegate or function to the given arguments depending on the held type, ensuring that all types are handled by the visiting functions.
    The handler supports multiple dispatch or multimethods: a feature of handler in which a function or method can be dynamically dispatched based on the run time (dynamic) type or, in the more general case, some other attribute of more than one of its arguments.
    Fuses algebraic types on return.
    Examples:
    static struct Asteroid { uint size; }
    static struct Spaceship { uint size; }
    alias SpaceObject = Variant!(Asteroid, Spaceship);
    
    alias collideWith = match!(
        (Asteroid x, Asteroid y) => "a/a",
        (Asteroid x, Spaceship y) => "a/s",
        (Spaceship x, Asteroid y) => "s/a",
        (Spaceship x, Spaceship y) => "s/s",
    );
    
    import mir.utility: min;
    
    // Direct access of a member in case of all algebraic types has this member
    alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1;
    
    alias collide = (x, y) => oops(x, y) ? "big-boom" : collideWith(x, y);
    
    auto ea = Asteroid(1);
    auto es = Spaceship(2);
    auto oa = SpaceObject(ea);
    auto os = SpaceObject(es);
    
    // Asteroid-Asteroid
    assert(collide(ea, ea) == "a/a");
    assert(collide(ea, oa) == "a/a");
    assert(collide(oa, ea) == "a/a");
    assert(collide(oa, oa) == "a/a");
    
    // Asteroid-Spaceship
    assert(collide(ea, es) == "a/s");
    assert(collide(ea, os) == "a/s");
    assert(collide(oa, es) == "a/s");
    assert(collide(oa, os) == "a/s");
    
    // Spaceship-Asteroid
    assert(collide(es, ea) == "s/a");
    assert(collide(es, oa) == "s/a");
    assert(collide(os, ea) == "s/a");
    assert(collide(os, oa) == "s/a");
    
    // Spaceship-Spaceship
    assert(collide(es, es) == "big-boom");
    assert(collide(es, os) == "big-boom");
    assert(collide(os, es) == "big-boom");
    assert(collide(os, os) == "big-boom");
    
    template tryMatch(visitors...)
    Behaves as match but doesn't enforce at compile time that all types can be handled by the visiting functions.
    Throws:
    Exception if naryFun!visitors can't be called with provided arguments
    Fuses algebraic types on return.
    Examples:
    import std.exception: assertThrown;
    static struct Asteroid { uint size; }
    static struct Spaceship { uint size; }
    alias SpaceObject = Variant!(Asteroid, Spaceship);
    
    alias collideWith = tryMatch!(
        (Asteroid x, Asteroid y) => "a/a",
        // No visitor for A/S pair 
        // (Asteroid x, Spaceship y) => "a/s",
        (Spaceship x, Asteroid y) => "s/a",
        (Spaceship x, Spaceship y) => "s/s",
    );
    
    import mir.utility: min;
    // Direct access of a member in case of all algebraic types has this member
    alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1;
    
    alias collide = (x, y) => oops(x, y) ? "big-boom" : collideWith(x, y);
    
    auto ea = Asteroid(1);
    auto es = Spaceship(2);
    auto oa = SpaceObject(ea);
    auto os = SpaceObject(es);
    
    // Asteroid-Asteroid
    assert(collide(ea, ea) == "a/a");
    assert(collide(ea, oa) == "a/a");
    assert(collide(oa, ea) == "a/a");
    assert(collide(oa, oa) == "a/a");
    
    // Asteroid-Spaceship
    assertThrown!Exception(collide(ea, es));
    assertThrown!Exception(collide(ea, os));
    assertThrown!Exception(collide(oa, es));
    assertThrown!Exception(collide(oa, os));
    
    // can deduce the type based on other return values
    static assert(is(typeof(collide(ea, os)) == string));
    static assert(is(typeof(collide(oa, es)) == string));
    static assert(is(typeof(collide(oa, os)) == string));
    
    // Also allows newer compilers to detect combinations which always throw an exception
    static if (is(typeof(collideWith(ea, es)) == noreturn))
    {
        static assert(is(typeof(collide(ea, es)) == string));
    }
    else
    {
        // not enough information to deduce the type from (ea, es) pair
        static assert(is(typeof(collide(ea, es)) == void));
    }
    
    // Spaceship-Asteroid
    assert(collide(es, ea) == "s/a");
    assert(collide(es, oa) == "s/a");
    assert(collide(os, ea) == "s/a");
    assert(collide(os, oa) == "s/a");
    
    // Spaceship-Spaceship
    assert(collide(es, es) == "big-boom");
    assert(collide(es, os) == "big-boom");
    assert(collide(os, es) == "big-boom");
    assert(collide(os, os) == "big-boom");
    
    template optionalMatch(visitors...)
    Behaves as match but doesn't enforce at compile time that all types can be handled by the visiting functions.
    Returns:
    nullable variant, null value is used if naryFun!visitors can't be called with provided arguments.
    Fuses algebraic types on return.
    Examples:
    static struct Asteroid { uint size; }
    static struct Spaceship { uint size; }
    alias SpaceObject = Variant!(Asteroid, Spaceship);
    
    alias collideWith = optionalMatch!(
        (Asteroid x, Asteroid y) => "a/a",
        // No visitor for A/S pair 
        // (Asteroid x, Spaceship y) => "a/s",
        (Spaceship x, Asteroid y) => "s/a",
        (Spaceship x, Spaceship y) => "s/s",
    );
    
    import mir.utility: min;
    // Direct access of a member in case of all algebraic types has this member
    alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1;
    
    alias collide = (x, y) => oops(x, y) ? "big-boom".nullable : collideWith(x, y);
    
    auto ea = Asteroid(1);
    auto es = Spaceship(2);
    auto oa = SpaceObject(ea);
    auto os = SpaceObject(es);
    
    // Asteroid-Asteroid
    assert(collide(ea, ea) == "a/a");
    assert(collide(ea, oa) == "a/a");
    assert(collide(oa, ea) == "a/a");
    assert(collide(oa, oa) == "a/a");
    
    // Asteroid-Spaceship
    // assert(collide(ea, es).isNull);  // Compiler error: incompatible types
    assert(collideWith(ea, es).isNull); // OK
    assert(collide(ea, os).isNull);
    assert(collide(oa, es).isNull);
    assert(collide(oa, os).isNull);
    
    
    // Spaceship-Asteroid
    assert(collide(es, ea) == "s/a");
    assert(collide(es, oa) == "s/a");
    assert(collide(os, ea) == "s/a");
    assert(collide(os, oa) == "s/a");
    
    // Spaceship-Spaceship
    assert(collide(es, es) == "big-boom");
    assert(collide(es, os) == "big-boom");
    assert(collide(os, es) == "big-boom");
    assert(collide(os, os) == "big-boom");
    
    // check types  
    
    static assert(!__traits(compiles, collide(Asteroid.init, Spaceship.init)));
    static assert(is(typeof(collideWith(Asteroid.init, Spaceship.init)) == Nullable!()));
    
    static assert(is(typeof(collide(Asteroid.init, Asteroid.init)) == Nullable!string));
    static assert(is(typeof(collide(Asteroid.init, SpaceObject.init)) == Nullable!string));
    static assert(is(typeof(collide(SpaceObject.init, Asteroid.init)) == Nullable!string));
    static assert(is(typeof(collide(SpaceObject.init, SpaceObject.init)) == Nullable!string));
    static assert(is(typeof(collide(SpaceObject.init, Spaceship.init)) == Nullable!string));
    static assert(is(typeof(collide(Spaceship.init, Asteroid.init)) == Nullable!string));
    static assert(is(typeof(collide(Spaceship.init, SpaceObject.init)) == Nullable!string));
    static assert(is(typeof(collide(Spaceship.init, Spaceship.init)) == Nullable!string));
    
    template autoMatch(visitors...)
    Behaves as match but doesn't enforce at compile time that all types can be handled by the visiting functions.
    Returns:
    optionally nullable type, null value is used if naryFun!visitors can't be called with provided arguments.
    Fuses algebraic types on return.
    Examples:
    static struct Asteroid { uint size; }
    static struct Spaceship { uint size; }
    alias SpaceObject = Variant!(Asteroid, Spaceship);
    
    alias collideWith = autoMatch!(
        (Asteroid x, Asteroid y) => "a/a",
        // No visitor for A/S pair 
        // (Asteroid x, Spaceship y) => "a/s",
        (Spaceship x, Asteroid y) => "s/a",
        (Spaceship x, Spaceship y) => "s/s",
    );
    
    import mir.utility: min;
    // Direct access of a member in case of all algebraic types has this member
    alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1;
    
    import mir.conv: to;
    alias collide = (x, y) => oops(x, y) ? "big-boom".to!(typeof(collideWith(x, y))) : collideWith(x, y);
    
    auto ea = Asteroid(1);
    auto es = Spaceship(2);
    auto oa = SpaceObject(ea);
    auto os = SpaceObject(es);
    
    // Asteroid-Asteroid
    assert(collide(ea, ea) == "a/a");
    assert(collide(ea, oa) == "a/a");
    assert(collide(oa, ea) == "a/a");
    assert(collide(oa, oa) == "a/a");
    
    // Asteroid-Spaceship
    // assert(collide(ea, es).isNull);  // Compiler error: incompatible types
    assert(collideWith(ea, es).isNull); // OK
    assert(collide(ea, os).isNull);
    assert(collide(oa, es).isNull);
    assert(collide(oa, os).isNull);
    
    // Spaceship-Asteroid
    assert(collide(es, ea) == "s/a");
    assert(collide(es, oa) == "s/a");
    assert(collide(os, ea) == "s/a");
    assert(collide(os, oa) == "s/a");
    
    // Spaceship-Spaceship
    assert(collide(es, es) == "big-boom");
    assert(collide(es, os) == "big-boom");
    assert(collide(os, es) == "big-boom");
    assert(collide(os, os) == "big-boom");
    
    // check types  
    
    static assert(!__traits(compiles, collide(Asteroid.init, Spaceship.init)));
    static assert(is(typeof(collideWith(Asteroid.init, Spaceship.init)) == Nullable!()));
    
    static assert(is(typeof(collide(Asteroid.init, Asteroid.init)) == string));
    static assert(is(typeof(collide(SpaceObject.init, Asteroid.init)) == string));
    static assert(is(typeof(collide(Spaceship.init, Asteroid.init)) == string));
    static assert(is(typeof(collide(Spaceship.init, SpaceObject.init)) == string));
    static assert(is(typeof(collide(Spaceship.init, Spaceship.init)) == string));
    
    static assert(is(typeof(collide(Asteroid.init, SpaceObject.init)) == Nullable!string));
    static assert(is(typeof(collide(SpaceObject.init, SpaceObject.init)) == Nullable!string));
    static assert(is(typeof(collide(SpaceObject.init, Spaceship.init)) == Nullable!string));
    
    template getMember(string member)
    Applies a member handler to the given Variant depending on the held type, ensuring that all types are handled by the visiting handler.
    Examples:
    static struct S { auto bar(int a) { return a; } enum boolean = true; }
    static struct C { alias bar = (double a) => a * 2; enum boolean = false; }
    
    alias V = Variant!(S, C);
    
    V x = S();
    V y = C();
    
    static assert(is(typeof(x.getMember!"bar"(2)) == Variant!(int, double)));
    assert(x.getMember!"bar"(2) == 2);
    assert(y.getMember!"bar"(2) != 4);
    assert(y.getMember!"bar"(2) == 4.0);
    
    // direct implementation
    assert(x.bar(2) == 2);
    assert(y.bar(2) != 4);
    assert(y.bar(2) == 4.0);
    assert(x.boolean);
    assert(!y.boolean);
    
    template matchMember(string member)
    Applies a member handler to the given Variant depending on the held type, ensuring that all types are handled by the visiting handler.
    Fuses algebraic types on return.
    Examples:
    static struct S
    {
        Nullable!int m;
    }
    
    static struct C
    {
        Variant!(float, double) m;
    }
    
    alias V = Variant!(S, C);
    
    V x = S(2.nullable);
    V y = C(Variant!(float, double)(4.0));
    
    // getMember returns an algebraic of algebaics
    static assert(is(typeof(x.getMember!"m") == Variant!(Variant!(float, double), Nullable!int)));
    // matchMember returns a fused algebraic
    static assert(is(typeof(x.matchMember!"m") == Nullable!(int, float, double)));
    assert(x.matchMember!"m" == 2);
    assert(y.matchMember!"m" != 4);
    assert(y.matchMember!"m" == 4.0);
    
    template tryGetMember(string member)
    Behaves as getMember but doesn't enforce at compile time that all types can be handled by the member visitor.
    Throws:
    Exception if member can't be accessed with provided arguments
    Examples:
    static struct S { int bar(int a) { return a; }}
    static struct C { alias Bar = (double a) => a * 2; }
    
    alias V = Variant!(S, C);
    
    V x = S();
    V y = C();
    
    static assert(is(typeof(x.tryGetMember!"bar"(2)) == int));
    static assert(is(typeof(y.tryGetMember!"Bar"(2)) == double));
    assert(x.tryGetMember!"bar"(2) == 2);
    assert(y.tryGetMember!"Bar"(2) == 4.0);
    
    Examples:
    alias Number = Variant!(int, double);
    
    Number x = Number(23);
    Number y = Number(1.0);
    
    assert(x.visit!((int v) => true, (float v) => false));
    assert(y.visit!((int v) => false, (float v) => true));
    
    template tryMatchMember(string member)
    Behaves as matchMember but doesn't enforce at compile time that all types can be handled by the member visitor.
    Throws:
    Exception if member can't be accessed with provided arguments
    Fuses algebraic types on return.
    template optionalGetMember(string member)
    Behaves as getMember but doesn't enforce at compile time that all types can be handled by the member visitor.
    Returns:
    nullable variant, null value is used if the member can't be called with provided arguments.
    template optionalMatchMember(string member)
    Behaves as matchMember but doesn't enforce at compile time that all types can be handled by the member visitor.
    Returns:
    nullable variant, null value is used if the member can't be called with provided arguments.
    Fuses algebraic types on return.
    template autoGetMember(string member)
    Behaves as getMember but doesn't enforce at compile time that all types can be handled by the member visitor.
    Returns:
    optionally nullable type, null value is used if the member can't be called with provided arguments.
    template autoMatchMember(string member)
    Behaves as matchMember but doesn't enforce at compile time that all types can be handled by the member visitor.
    Returns:
    optionally nullable type, null value is used if the member can't be called with provided arguments.
    Fuses algebraic types on return.
    template Err(T)

    auto err(T)(T value);
    Wrapper to denote an error value type.
    The wrapper is autostripped by none.
    See Also:
    Examples:
    @reflectErr static struct E {}
    
    static assert(is(Err!string == Err!string));
    static assert(is(Err!(Err!string) == Err!string));
    static assert(is(Err!E == E));
    static assert(is(Err!Exception == Exception));
    
    static assert(is(typeof("str".err) == Err!string));
    static assert(is(typeof(E().err) == E));
    static assert(is(typeof(new Exception("str").err) == Exception));
    
    struct Err;
    T value;
    template stripErr(T)
    Strips out !(LREF Err) wrapper from the type.
    Examples:
    static assert(is(stripErr!Exception == Exception));
    static assert(is(stripErr!string == string));
    static assert(is(stripErr!(Err!string) == string));
    
    template suit(alias filter, visitors...)
    See Also:
    some and none.
    Parameters:
    visitors visitors to match with.
    Examples:
    import std.traits: isDynamicArray, Unqual;
    import std.meta: templateNot;
    alias V = Variant!(long, int, string, long[], int[]);
    alias autoGetElementType = match!(
        (string s) => "string", // we override the suit handler below for string
        suit!(isDynamicArray, a => Unqual!(typeof(a[0])).stringof), 
        suit!(templateNot!isDynamicArray, a => Unqual!(typeof(a)).stringof), 
    );
    assert(autoGetElementType(V(string.init)) == "string");
    assert(autoGetElementType(V((long[]).init)) == "long");
    assert(autoGetElementType(V((int[]).init)) == "int");
    assert(autoGetElementType(V(long.init)) == "long");
    assert(autoGetElementType(V(int.init)) == "int");
    
    Examples:
    import std.traits: allSameType;
    import std.meta: templateNot;
    
    static struct Asteroid { uint size; }
    static struct Spaceship { uint size; }
    alias SpaceObject = Variant!(Asteroid, Spaceship);
    
    auto errorMsg = "can't unite an asteroid with a spaceship".err;
    
    alias unite = match!(
        suit!(allSameType, (a, b) => typeof(a)(a.size + b.size)),
        suit!(templateNot!allSameType, (a, b) => errorMsg),
    );
    
    auto ea = Asteroid(10);
    auto es = Spaceship(1);
    auto oa = SpaceObject(ea);
    auto os = SpaceObject(es);
    
    static assert(is(typeof(unite(oa, oa)) == Variant!(Err!string, Asteroid, Spaceship)));
    
    // Asteroid-Asteroid
    assert(unite(ea, ea) == Asteroid(20));
    assert(unite(ea, oa) == Asteroid(20));
    assert(unite(oa, ea) == Asteroid(20));
    assert(unite(oa, oa) == Asteroid(20));
    
    // Asteroid-Spaceship
    assert(unite(ea, es) == errorMsg);
    assert(unite(ea, os) == errorMsg);
    assert(unite(oa, es) == errorMsg);
    assert(unite(oa, os) == errorMsg);
    
    // Spaceship-Asteroid
    assert(unite(es, ea) == errorMsg);
    assert(unite(es, oa) == errorMsg);
    assert(unite(os, ea) == errorMsg);
    assert(unite(os, oa) == errorMsg);
    
    // Spaceship-Spaceship
    assert(unite(es, es) == Spaceship(2));
    assert(unite(es, os) == Spaceship(2));
    assert(unite(os, es) == Spaceship(2));
    assert(unite(os, os) == Spaceship(2));
    
    template some(visitors...)

    template none(visitors...)
    some is a variant of suit that forces that type of any argument doesn't satisfy isErr template.
    none is a variant of suit that forces that type of all arguments satisfy isErr template. The handler automatically strips the Err wrapper.
    Parameters:
    visitors visitors to match with.
    Examples:
    import mir.conv: to;
    
    alias orElse(alias fun) = visit!(some!"a", none!fun);
    alias errToString = orElse!(to!string);
    
    // can any other type including integer enum
    @reflectErr
    static struct ErrorInfo {
        string msg;
        auto toString() const { return msg; }
    }
    
    alias V = Variant!(Err!string, ErrorInfo, Exception, long, double);
    alias R = typeof(errToString(V.init));
    
    static assert(is(R == Variant!(string, long, double)), R.stringof);
    
    {
        V v = 1;
        assert(v.isOk);
        assert(errToString(v) == 1);
    }
    
    {
        V v = 1.0;
        assert(v.isOk);
        assert(errToString(v) == 1.0);
    }
    
    {
        V v = ErrorInfo("msg");
        assert(!v.isOk);
        assert(errToString(v) == "msg");
    }
    
    {
        V v = "msg".err;
        assert(!v.isOk);
        assert(errToString(v) == "msg");
    }
    
    {
        V v = new Exception("msg"); enum line = __LINE__;
        assert(!v.isOk);
        assert(errToString(v) == "object.Exception@" ~ __FILE__ ~ "(" ~ line.stringof ~ "): msg");
    }
    
    enum reflectErr;
    Attribute that denotes an error type. Can be used with some and none.
    See Also:
    template isErr(T)
    Checks if T is a instance of Err or if it is annotated with reflectErr.
    template isResultVariant(T)
    Checks if T is a Variant with at least one allowed type that satisfy isErr traits.
    template SomeVariant(T : Algebraic!Types, Types...)
    Gets subtype of algebraic without types for which isErr is true.
    Examples:
    @reflectErr static struct ErrorS { }
    alias V = Variant!(ErrorS, Err!string, long, double, This[]);
    static assert(is(SomeVariant!V == Variant!(long, double, This[])));
    
    template NoneVariant(T : Algebraic!Types, Types...)
    Gets subtype of algebraic with types for which isErr is true.
    Examples:
    @reflectErr static struct ErrorS { }
    alias V = Variant!(ErrorS, Err!string, long, double, This[]);
    static assert(is(NoneVariant!V == Variant!(ErrorS, Err!string)));
    
    template assumeOk(alias visitor = naryFun!("", "a"), alias handler = .match)
    Validates that the result doesn't contain an error value.
    Parameters:
    visitor (compiletime) visitor function. Default value is naryFun!("", "a").
    handler (compiletime) visitor handler to use. Default value is match.
    Throws:
    Throws an exception if at least one parameter passed to visitor satisfies isErr traits. If there is only one paramter (common case) and its value is Throwable, throws it. Otherwise, all_ paramters will be printed to the exception message using mir.format.print.
    Examples:
    import std.exception: collectExceptionMsg;
    import mir.exception: MirException;
    
    alias SingleTypeValue = typeof(assumeOk(Variant!(Exception, long).init));
    static assert(is(SingleTypeValue == long), SingleTypeValue.stringof);
    
    
    // can any other type including integer enum
    @reflectErr
    static struct ErrorInfo {
        string msg;
        auto toString() const { return msg; }
    }
    
    alias V = Variant!(Err!string, ErrorInfo, Exception, long, double);
    alias R = typeof(assumeOk(V.init));
    
    static assert(is(R == Variant!(long, double)), R.stringof);
    
    {
        V v = 1;
        assert(v.isOk);
        assert(v.assumeOk == 1);
    }
    
    {
        V v = 1.0;
        assert(v.isOk);
        assert(v.assumeOk == 1.0);
    }
    
    {
        V v = ErrorInfo("msg");
        assert(!v.isOk);
        assert(v.assumeOk.collectExceptionMsg == "assumeOk failure:\nmsg");
    }
    
    {
        V v = "msg".err;
        assert(!v.isOk);
        assert(v.assumeOk.collectExceptionMsg == "assumeOk failure:\nmsg");
    }
    
    {
        V v = new Exception("msg");
        assert(!v.isOk);
        assert(v.assumeOk.collectExceptionMsg == "msg");
    }