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.
Name | Description |
---|---|
Variant | an algebraic type |
TaggedVariant | a tagged algebraic type |
Nullable | an algebraic type with at least typeof(null) |
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 |
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. |
Err | Wrapper to denote an error value type. |
reflectErr | Attribute that denotes that the type is an error value type. |
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). |
isTypeSet | Checks if the types are the same as TypeSet of them. |
ValueTypeOfNullable | Gets type of |
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
- 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
See Also:
License:
Authors:
Ilia Ki
- enum
algMeta
; - The attribute is used to define a permanent member field in an anlgebraic type. Should applied to a field of the union passed to TaggedVariant.
- enum
algTransp
; - The attribute is used in pair with algMeta to exclude the field from compression in toHash, opEquals, and opCmp methods.
- enum
algVerbose
; - The attribute is used in pair with algMeta to use the field as an error infomration. Usually it is a position marker in a file. The type should have scope const toString method.
- 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
isLikeVariant
(T); - Same as isVariant, but matches for alias this variant types (requires DMD FE 2.100.0 or later)Examples:
static struct CustomVariant { Variant!(int, string) data; alias data this; this(T)(T v) { data = v; } ref typeof(this) opAssign(T)(T v) { data = v; return this; } } static assert(isLikeVariant!(Variant!(int, string))); static assert(isLikeVariant!(const Variant!(int[], string))); static assert(isLikeVariant!(Nullable!(int, string))); static assert(!isLikeVariant!int); static assert(!isVariant!CustomVariant); static assert(isLikeVariant!CustomVariant); CustomVariant customVariant = 5; assert(customVariant.match!( (string s) => false, (int n) => true ));
- enum bool
isTaggedVariant
(T); - Checks if the type is instance of tagged Algebraic.Tagged algebraics can be defined with TaggedVariant.Examples:
static union MyUnion { int integer; immutable(char)[] string; } alias MyAlgebraic = Algebraic!MyUnion; static assert(isTaggedVariant!MyAlgebraic); static assert(!isTaggedVariant!int); static assert(!isTaggedVariant!(Variant!(int, string)));
- enum bool
isLikeTaggedVariant
(T); - Same as isTaggedVariant, but with support for custom alias this variants.Only works since DMD FE 2.100, see isLikeVariant.
- enum bool
isNullable
(T); - 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);
- enum bool
isLikeNullable
(T); - Same as isNullable, but with support for custom alias this variants.Only works since DMD FE 2.100, see isLikeVariant.
- 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
; - template
TypeSet
(T...) - Type set resolution template used to construct Algebraic .
- 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 implementation 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 Variantstatic 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 setVariant!() a; auto b = a; assert(a.toHash == 0); assert(a == b); assert(a <= b && b >= a); static assert(typeof(a).sizeof == 1);
Examples:Small typesstatic struct S { ubyte d; } static assert(Nullable!(byte, char, S).sizeof == 2);
- template
TaggedVariant
(T) if (is(T == union)) - Tagged Variant Type (aka Tagged Algebraic Type).Compatible with BetterC mode. Template has two declarations:
// and template TaggedVariant(T) if (is(T == union)) { ... }
See Also:Examples:Json Value with stylesenum Style { block, flow } static struct SomeMetadata { int a; @safe pure nothrow @nogc scope int opCmp(scope const SomeMetadata rhs) const { return a - rhs.a; } } static struct ParsePosition { string file, line, column; void toString()(scope ref W w) scope const { w.put(file); if (line) { w.put("("); w.put(line); if (column) { w.put(","); w.put(column); } w.put(")"); } } } static union Json_ { typeof(null) null_; bool boolean; long integer; double floating; // Not, that `string` is't builtin type but an alias in `object.d` // So we can use `string` as a name of the string field immutable(char)[] string; This[] array; // commented out to test `opCmp` primitive // This[immutable(char)[]] object; @algMeta: bool active; SomeMetadata metadata; @algTransp: Style style; @algVerbose ParsePosition position; } alias JsonAlgebraic = Algebraic!Json_; // typeof(null) has priority static assert(JsonAlgebraic.Kind.init == JsonAlgebraic.Kind.null_); static assert(JsonAlgebraic.Kind.null_ == 0); // Kind and AllowedTypes has the same order static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.array] == JsonAlgebraic[])); static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.boolean] == bool)); static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.floating] == double)); static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.integer] == long)); static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.null_] == typeof(null))); // static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.object] == JsonAlgebraic[string])); JsonAlgebraic v; assert(v.kind == JsonAlgebraic.Kind.null_); v = 1; assert(v.kind == JsonAlgebraic.Kind.integer); assert(v == 1); v = JsonAlgebraic(1); assert(v == 1); v = v.get!(long, double); v = "Tagged!"; // member-based access. Simple! assert(v.string == "Tagged!"); // type-based access assert(v.get!string == "Tagged!"); assert(v.trustedGet!string == "Tagged!"); assert(v.kind == JsonAlgebraic.Kind.string); assert(v.get!"string" == "Tagged!"); // string-based get assert(v.trustedGet!"string" == "Tagged!"); // string-based trustedGet assert(v.get!(JsonAlgebraic.Kind.string) == "Tagged!"); // Kind-based get assert(v.trustedGet!(JsonAlgebraic.Kind.string) == "Tagged!"); // Kind-based trustedGet // checks assert(v._is!string); // type based assert(v._is!"string"); // string based assert(v._is!(JsonAlgebraic.Kind.string)); // v = null; assert(v.kind == JsonAlgebraic.Kind.null_); v = [JsonAlgebraic("str"), JsonAlgebraic(4.3)]; assert(v.kind == JsonAlgebraic.Kind.array); assert(v.trustedGet!(JsonAlgebraic[])[1].kind == JsonAlgebraic.Kind.floating); JsonAlgebraic w = v; w.style = Style.flow; assert(v.style != w.style); assert(v == w); assert(v <= w); assert(v >= w); assert(v.toHash == w.toHash); w.active = true; assert(v != w); assert(v.toHash != w.toHash); assert(v.get!"array" == w.get!"array"); assert(v < w); // test equality with self-referencing allowed type auto arr = [JsonAlgebraic("str"), JsonAlgebraic(120)]; v = arr; assert(v == arr); assert(v == [JsonAlgebraic("str"), JsonAlgebraic(120)]);
Examples:Wrapped algebraic with propogated primitivesstatic struct Response { private union Response_ { double float_; immutable(char)[] string; Response[] array; Response[immutable(char)[]] table; } alias ResponseAlgebraic = Algebraic!Response_; ResponseAlgebraic data; alias Tag = ResponseAlgebraic.Kind; // propogates opEquals, opAssign, and other primitives alias data this; static foreach (T; ResponseAlgebraic.AllowedTypes) this(T v) @safe pure nothrow @nogc { data = v; } } Response v = 3.0; assert(v.kind == Response.Tag.float_); v = "str"; assert(v == "str");
- template
Nullable
(T...)
Nullable!Tnullable
(T)(Tt
); - Nullable Variant Type (aka Algebraic Type).The implementation 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 typeNullable
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 supportNullable!() a; auto b = a; assert(a.toHash == 0); assert(a == b); assert(a <= b && b >= a); static assert(typeof(a).sizeof == 1);
- struct
Algebraic
(T__...); - 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 Cc { // alias this members are supported Base base; alias base this; int a; private string _b; @safe pure nothrow @nogc: override size_t toHash() scope const { return hashOf(_b) ^ a; } string b() const @property { return _b; } void b(string b) @property { _b = b; } int retArg(int v) { return v; } string retArgT(TArgs...)(int v) { return TArgs.stringof; } this(int a, string b) { this.a = a; this._b = b; } } static struct S { string b; int a; double retArg(double v) { return v; } double retArgT(TArgs...)(int v) { return v * TArgs.length; } // alias this members are supported Base base; alias base this; } static void inc(ref int a) { a++; } alias V = Nullable!(Cc, S); // or Variant! auto v = V(2, "str"); assert(v._is!Cc); 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); // method with template args support assert(v.retArgT!dchar(100)._is!string); assert(v.retArgT!dchar(100) == "(dchar)"); 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);
- static immutable char[][]
metaFieldNames__
; - static immutable char[][]
typeFieldNames__
; - alias
AllowedTypes
= AliasSeq!(ReplaceTypeUnless!(.isVariant, .This, Algebraic!T__, Types__)); - Allowed types listSee 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_ti
, size_tj
)
if (dimension == 0); - Returns:slice type of Slice!(IotaIterator!size_t)
- this(RhsTypes...)(Algebraic!RhsTypes
rhs
)
if (!(hasElaborateCopyConstructor!(Algebraic!T__) && is(Algebraic!RhsTypes == typeof(this))) && 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 pure nothrow @nogc scope @trusted size_t
toHash
(); - const pure nothrow @nogc scope @trusted bool
opEquals
()(scope const Algebraicrhs
);
const pure nothrow @nogc scope @trusted boolopEquals
()(ref scope const Algebraicrhs
); - const pure nothrow @nogc scope @trusted int
opCmp
()(auto ref scope const typeof(this)rhs
); - const scope @trusted string
toString
()();
const pure scope @trusted voidtoString
(W)(ref scope Ww
)
if (__traits(compiles, () pure {w
.put("Algebraic"); } ));
const scope @trusted voidtoString
(W)(ref scope Ww
)
if (!__traits(compiles, () pure {w
.put("Algebraic"); } )); - Requires mir-algorithm package
- const bool
opCast
(C)()
if (is(C == bool)); - const Algebraic
opCast
(C)()
if (is(C == Algebraic)); - const @property bool
isNull
();
voidnullify
();
autoget
()()
if (allSatisfy!(isCopyable, AllowedTypes[1 .. $]) && (AllowedTypes.length != 2) && is(AllowedTypes[0] == typeof(null))); - 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. Withoutfallback
, callingget
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));
templatetrustedGet
(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)
templatetrustedGet
(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));
templateget
(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)
templateget
(string kind) get
overload that accept .Algebraic.Kind.- template
_is
(Kind kind)
template_is
(string kind) _is
overload that accept .Algebraic.Kind.- ref @trusted auto
opAssign
(RhsTypes...)(Algebraic!RhsTypesrhs
) return
if (allSatisfy!(Contains!AllowedTypes, Algebraic!RhsTypes.AllowedTypes) && !is(Algebraic == Algebraic!RhsTypes)); - 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 propagationVariant!(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 packageimport 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.toString == "null"); variant = V._void; assert(variant._is!void); assert(is(typeof(variant.get!void()) == void)); assert(secondOrderVisitorHandler(visitorHandler(variant)) == "VOID"); assert(variant.toString == "void"); variant = 5; assert(secondOrderVisitorHandler(visitorHandler(variant)) == "SO VOID"); assert(variant == 6); assert(variant.toString == (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 argumentsExamples:
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.See Also: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, TArgs...) - 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 C2 { alias bar = (double a) => a * 2; enum boolean = false; } alias V = Variant!(S, C2); V x = S(); V y = C2(); 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, TArgs...) - 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 C1 { Variant!(float, double) m; } alias V = Variant!(S, C1); V x = S(2.nullable); V y = C1(Variant!(float, double)(4.0)); // getMember returns an algebraic of algebraics 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 argumentsExamples:
static struct S { int bar(int a) { return a; }} static struct C3 { alias Bar = (double a) => a * 2; } alias V = Variant!(S, C3); V x = S(); V y = C3(); 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, TArgs...) - 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, TArgs...) - 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, TArgs...) - 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, TArgs...) - 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, TArgs...) - 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)
autoerr
(T)(Tvalue
); - 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 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...) -
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...)
templatenone
(visitors...) - none is a variant of suit that forces that type of all arguments satisfy isErr template. The handler automatically strips the Err wrapper.See Also: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
; - See Also:Err.
- 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"); }
Copyright © 2016-2023 by Ilya Yaroshenko | Page generated by
Ddoc on Mon Nov 6 15:24:33 2023