The OpenD Programming Language

1 /++
2 $(H2 Variant and Nullable types)
3 
4 This module implements a
5 $(HTTP erdani.org/publications/cuj-04-2002.php.html,discriminated union)
6 type (a.k.a.
7 $(HTTP en.wikipedia.org/wiki/Tagged_union,tagged union),
8 $(HTTP en.wikipedia.org/wiki/Algebraic_data_type,algebraic type)).
9 Such types are useful
10 for type-uniform binary interfaces, interfacing with scripting
11 languages, and comfortable exploratory programming.
12 
13 The module defines generic $(LREF Algebraic) type that contains a payload.
14 The allowed types of the paylad are defined by the unordered $(LREF TypeSet).
15 
16 $(LREF Algebraic) template accepts two arguments: self type set id and a list of type sets.
17 
18 $(BOOKTABLE $(H3 $(LREF Algebraic) Aliases),
19 $(TR $(TH Name) $(TH Description))
20 $(T2 Variant, an algebraic type)
21 $(T2 TaggedVariant, a tagged algebraic type)
22 $(T2 Nullable, an algebraic type with at least `typeof(null)`)
23 )
24 
25 $(BOOKTABLE $(H3 Visitor Handlers),
26 $(TR $(TH Name) $(TH Ensures can match) $(TH Throws if no match) $(TH Returns $(LREF Nullable)) $(TH Multiple dispatch) $(TH Argumments count) $(TH Fuses Algebraic types on return))
27 $(LEADINGROWN 8, Classic handlers)
28 $(T8 visit, Yes, N/A, No, No, 1+, No)
29 $(T8 optionalVisit, No, No, Yes, No, 1+, No)
30 $(T8 autoVisit, No, No, auto, No, 1+, No)
31 $(T8 tryVisit, No, Yes, No, No, 1+, No)
32 $(LEADINGROWN 8, Multiple dispatch and algebraic fusion on return)
33 $(T8 match, Yes, N/A, No, Yes, 0+, Yes)
34 $(T8 optionalMatch, No, No, Yes, Yes, 0+, Yes)
35 $(T8 autoMatch, No, No, auto, Yes, 0+, Yes)
36 $(T8 tryMatch, No, Yes, No, Yes, 0+, Yes)
37 $(LEADINGROWN 8,  Inner handlers. Multiple dispatch and algebraic fusion on return.)
38 $(T8 suit, N/A(Yes), N/A, No, Yes, ?, Yes)
39 $(T8 some, N/A(Yes), N/A, No, Yes, 0+, Yes)
40 $(T8 none, N/A(Yes), N/A, No, Yes, 1+, Yes)
41 $(T8 assumeOk, Yes(No), No(Yes), No(Yes), Yes(No), 0+, Yes(No))
42 $(LEADINGROWN 8, Member access)
43 $(T8 getMember, Yes, N/A, No, No, 1+, No)
44 $(T8 optionalGetMember, No, No, Yes, No, 1+, No)
45 $(T8 autoGetMember, No, No, auto, No, 1+, No)
46 $(T8 tryGetMember, No, Yes, No, No, 1+, No)
47 $(LEADINGROWN 8, Member access with algebraic fusion on return)
48 $(T8 matchMember, Yes, N/A, No, No, 1+, Yes)
49 $(T8 optionalMatchMember, No, No, Yes, No, 1+, Yes)
50 $(T8 autoMatchMember, No, No, auto, No, 1+, Yes)
51 $(T8 tryMatchMember, No, Yes, No, No, 1+, Yes)
52 )
53 
54 $(BOOKTABLE $(H3 Special Types),
55 $(TR $(TH Name) $(TH Description))
56 $(T2plain `void`, It is usefull to indicate a possible return type of the visitor. Can't be accesed by reference. )
57 $(T2plain `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. )
58 $(T2 This, Dummy structure that is used to construct self-referencing algebraic types. Example: `Variant!(int, double, string, This*[2])`)
59 $(T2plain $(LREF SetAlias)`!setId`, Dummy structure that is used to construct cyclic-referencing lists of algebraic types. )
60 $(T2 Err, Wrapper to denote an error value type. )
61 $(T2 reflectErr, Attribute that denotes that the type is an error value type. )
62 )
63 
64 $(BOOKTABLE $(H3 $(LREF Algebraic) Traits),
65 $(TR $(TH Name) $(TH Description))
66 $(T2 isVariant, Checks if the type is instance of $(LREF Algebraic).)
67 $(T2 isNullable, Checks if the type is instance of $(LREF Algebraic) with a self $(LREF TypeSet) that contains `typeof(null)`. )
68 $(T2 isTypeSet, Checks if the types are the same as $(LREF TypeSet) of them. )
69 $(T2 ValueTypeOfNullable, Gets type of $(LI $(LREF .Algebraic.get.2)) method. )
70 $(T2 SomeVariant, Gets subtype of algebraic without types for which $(LREF isErr) is true.)
71 $(T2 NoneVariant, Gets subtype of algebraic with types for which $(LREF isErr) is true.)
72 $(T2 isErr, Checks if T is a instance of $(LREF Err) or if it is annotated with $(LREF reflectErr).)
73 $(T2 isResultVariant, Checks if T is a Variant with at least one allowed type that satisfy $(LREF isErr) traits.)
74 
75 )
76 
77 
78 $(H3 Type Set)
79 $(UL
80 $(LI Type set is unordered. Example:`TypeSet!(int, double)` and `TypeSet!(double, int)` are the same. )
81 $(LI Duplicats are ignored. Example: `TypeSet!(float, int, float)` and `TypeSet!(int, float)` are the same. )
82 $(LI Types are automatically unqualified if this operation can be performed implicitly. Example: `TypeSet!(const int) and `TypeSet!int` are the same. )
83 $(LI Non trivial `TypeSet!(A, B, ..., etc)` is allowed.)
84 $(LI Trivial `TypeSet!T` is allowed.)
85 $(LI Empty `TypeSet!()` is allowed.)
86 )
87 
88 $(H3 Visitors)
89 $(UL
90 $(LI Visitors are allowed to return values of different types If there are more then one return type then the an $(LREF Algebraic) type is returned. )
91 $(LI Visitors are allowed to accept additional arguments. The arguments can be passed to the visitor handler. )
92 $(LI Multiple visitors can be passes to the visitor handler. )
93 $(LI Visitors are matched according to the common $(HTTPS dlang.org/spec/function.html#function-overloading, Dlang Function Overloading) rules. )
94 $(LI Visitors are allowed accept algebraic value by reference except the value of `typeof(null)`. )
95 $(LI Visitors are called without algebraic value if its algebraic type is `void`. )
96 $(LI 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. )
97 )
98 
99 $(H3 Implementation Features)
100 $(UL
101 $(LI BetterC support. Runtime `TypeInfo` is not used.)
102 $(LI Copy-constructors and postblit constructors are supported. )
103 $(LI `toHash`, `opCmp`. `opEquals`, and `toString` support. )
104 $(LI No string or template mixins are used. )
105 $(LI Optimised for fast execution. )
106 $(LI $(LREF some) / $(LREF none) idiom. )
107 )
108 
109 See_also: $(HTTPS en.wikipedia.org/wiki/Algebra_of_sets, Algebra of sets).
110 
111 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
112 Authors: Ilia Ki
113 
114 Macros:
115 T2plain=$(TR $(TDNW $1) $(TD $+))
116 T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
117 T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4))
118 T8=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4) $(TD $5) $(TD $6) $(TD $7) $(TD $8))
119 
120 +/
121 module mir.algebraic;
122 
123 import mir.internal.meta;
124 import mir.functional: naryFun;
125 import mir.exception: toMutable;
126 
127 /++
128 The attribute is used to define a permanent member field in an anlgebraic type.
129 Should applied to a field of the union passed to $(LREF TaggedVariant).
130 +/
131 enum algMeta;
132 /++
133 The attribute is used in pair with $(LREF algMeta) to exclude the field
134 from compression in `toHash`, `opEquals`, and `opCmp` methods.
135 +/
136 enum algTransp;
137 /++
138 The attribute is used in pair with $(LREF algMeta) to use the field
139 as an error infomration. Usually it is a position marker in a file.
140 The type should have `scope const` `toString` method.
141 +/
142 enum algVerbose;
143 
144 private static immutable variantExceptionMsg = "mir.algebraic: the algebraic stores other type then requested.";
145 private static immutable variantNullExceptionMsg = "mir.algebraic: the algebraic is empty and doesn't store any value.";
146 private static immutable variantMemberExceptionMsg = "mir.algebraic: the algebraic stores a type that isn't compatible with the user provided visitor and arguments.";
147 
148 version (D_Exceptions)
149 {
150     private static immutable variantException = new Exception(variantExceptionMsg);
151     private static immutable variantNullException = new Exception(variantNullExceptionMsg);
152     private static immutable variantMemberException = new Exception(variantMemberExceptionMsg);
153 }
154 
155 private static struct _Null()
156 {
157 @safe pure nothrow @nogc const:
158     int opCmp(_Null) { return 0; }
159     this(typeof(null)) inout {}
160     string toString() { return "null"; }
161 }
162 
163 private static struct _Void()
164 {
165  @safe pure nothrow @nogc const:
166     int opCmp(_Void) { return 0; }
167     string toString() { return "void"; }
168 }
169 
170 /++
171 Checks if the type is instance of $(LREF Algebraic).
172 +/
173 enum bool isVariant(T) = is(immutable T == immutable Algebraic!Types, Types...);
174 
175 ///
176 @safe pure version(mir_core_test) unittest
177 {
178     static assert(isVariant!(Variant!(int, string)));
179     static assert(isVariant!(const Variant!(int[], string)));
180     static assert(isVariant!(Nullable!(int, string)));
181     static assert(!isVariant!int);
182 }
183 
184 /++
185 Same as $(LREF isVariant), but matches for `alias this` variant types (requires
186 DMD FE 2.100.0 or later)
187 +/
188 enum bool isLikeVariant(T) = !is(immutable T == immutable noreturn)
189     && is(immutable T : immutable Algebraic!Types, Types...);
190 
191 
192 static if (__VERSION__ >= 2_100)
193 {
194     ///
195     @safe pure version(mir_core_test) unittest
196     {
197         static struct CustomVariant
198         {
199             Variant!(int, string) data;
200             alias data this;
201             this(T)(T v) { data = v; }
202             ref typeof(this) opAssign(T)(T v)
203             {
204                 data = v;
205                 return this;
206             }
207         }
208 
209         static assert(isLikeVariant!(Variant!(int, string)));
210         static assert(isLikeVariant!(const Variant!(int[], string)));
211         static assert(isLikeVariant!(Nullable!(int, string)));
212         static assert(!isLikeVariant!int);
213 
214         static assert(!isVariant!CustomVariant);
215         static assert(isLikeVariant!CustomVariant);
216 
217         CustomVariant customVariant = 5;
218         assert(customVariant.match!(
219             (string s) => false,
220             (int n) => true
221         ));
222     }
223 }
224 
225 /++
226 Checks if the type is instance of tagged $(LREF Algebraic).
227 
228 Tagged algebraics can be defined with $(LREF TaggedVariant).
229 +/
230 enum bool isTaggedVariant(T) = is(immutable T == immutable Algebraic!U, U) && is(U == union);
231 
232 ///
233 @safe pure version(mir_core_test) unittest
234 {
235     static union MyUnion
236     {
237         int integer;
238         immutable(char)[] string;
239     }
240 
241     alias MyAlgebraic = Algebraic!MyUnion;
242     static assert(isTaggedVariant!MyAlgebraic);
243 
244     static assert(!isTaggedVariant!int);
245     static assert(!isTaggedVariant!(Variant!(int, string)));
246 }
247 
248 /++
249 Same as $(LREF isTaggedVariant), but with support for custom `alias this`
250 variants.
251 
252 Only works since DMD FE 2.100, see $(LREF isLikeVariant).
253 +/
254 enum bool isLikeTaggedVariant(T) = isLikeVariant!T && is(T.Kind == enum);
255 
256 /++
257 Checks if the type is instance of $(LREF Algebraic) with a self $(LREF TypeSet) that contains `typeof(null)`.
258 +/
259 enum bool isNullable(T) = is(immutable T == immutable Algebraic!(typeof(null), Types), Types...);
260 
261 ///
262 @safe pure version(mir_core_test) unittest
263 {
264     static assert(isNullable!(const Nullable!(int, string)));
265     static assert(isNullable!(Nullable!()));
266 
267     static assert(!isNullable!(Variant!()));
268     static assert(!isNullable!(Variant!string));
269     static assert(!isNullable!int);
270     static assert(!isNullable!string);
271 }
272 
273 /++
274 Same as $(LREF isNullable), but with support for custom `alias this` variants.
275 
276 Only works since DMD FE 2.100, see $(LREF isLikeVariant).
277 +/
278 enum bool isLikeNullable(T) = !is(immutable T == immutable noreturn)
279     && is(immutable T : immutable Algebraic!(typeof(null), Types), Types...);
280 
281 /++
282 Gets type of $(LI $(LREF .Algebraic.get.2)) method.
283 +/
284 template ValueTypeOfNullable(T : Algebraic!(typeof(null), Types), Types...)
285 {
286     static if (Types.length == 1)
287         alias ValueTypeOfNullable = Types[0];
288     else
289         alias ValueTypeOfNullable = Algebraic!Types;
290 }
291 
292 ///
293 @safe pure version(mir_core_test) unittest
294 {
295     static assert(is(ValueTypeOfNullable!(const Nullable!(int, string)) == Algebraic!(int, string)));
296     static assert(is(ValueTypeOfNullable!(Nullable!()) == Algebraic!()));
297     static assert(is(typeof(Nullable!().get()) == Algebraic!()));
298 }
299 
300 /++
301 Dummy type for $(LREF Variant) and $(LREF Nullable) self-referencing.
302 +/
303 struct This
304 {
305 @safe pure nothrow @nogc scope const:
306     int opCmp(typeof(this)) { return 0; }
307     string toString() { return typeof(this).stringof; }
308 }
309 
310 private template TagInfo(T, string name, udas...)
311     if (udas.length <= 3)
312 {
313     import std.meta: staticIndexOf;
314     alias Type = T;
315     enum tag = name;
316     enum meta = staticIndexOf!(algMeta, udas) >= 0;
317     enum transparent = staticIndexOf!(algTransp, udas) >= 0;
318     enum verbose = staticIndexOf!(algVerbose, udas) >= 0;
319 }
320 
321 // example from std.variant
322 /++
323 $(H4 Self-Referential Types)
324 A useful and popular use of algebraic data structures is for defining
325 $(LUCKY self-referential data structures), i.e. structures that embed references to
326 values of their own type within.
327 This is achieved with $(LREF Variant) by using $(LREF This) as a placeholder whenever a
328 reference to the type being defined is needed. The $(LREF Variant) instantiation
329 will perform
330 $(LINK2 https://en.wikipedia.org/wiki/Name_resolution_(programming_languages)#Alpha_renaming_to_make_name_resolution_trivial,
331 alpha renaming) on its constituent types, replacing $(LREF This)
332 with the self-referenced type. The structure of the type involving $(LREF This) may
333 be arbitrarily complex.
334 +/
335 @safe pure version(mir_core_test) unittest
336 {
337     import mir.functional: Tuple;
338 
339     // A tree is either a leaf or a branch of two others
340     alias Tree(Leaf) = Variant!(Leaf, Tuple!(This*, This*));
341     alias Leafs = Tuple!(Tree!int*, Tree!int*);
342 
343     Tree!int tree = Leafs(new Tree!int(41), new Tree!int(43));
344     Tree!int* right = tree.get!Leafs[1];
345     assert(*right == 43);
346 }
347 
348 ///
349 @safe pure version(mir_core_test) unittest
350 {
351     // An object is a double, a string, or a hash of objects
352     alias Obj = Variant!(double, string, This[string], This[]);
353     alias Map = Obj[string];
354 
355     Obj obj = "hello";
356     assert(obj._is!string);
357     assert(obj.trustedGet!string == "hello");
358     obj = 42.0;
359     assert(obj.get!double == 42);
360     obj = ["customer": Obj("John"), "paid": Obj(23.95)];
361     assert(obj.get!Map["customer"] == "John");
362 }
363 
364 /++
365 Type set resolution template used to construct $(LREF Algebraic) .
366 +/
367 template TypeSet(T...)
368 {
369     import std.meta: staticSort, staticMap, allSatisfy, anySatisfy;
370     // sort types by sizeof and them mangleof
371     // but typeof(null) goes first
372     static if (is(staticMap!(TryRemoveConst, T) == T))
373         static if (is(NoDuplicates!T == T))
374             static if (staticIsSorted!(TypeCmp, T))
375             {
376                 alias TypeSet = T;
377             }
378             else
379                 alias TypeSet = .TypeSet!(staticSort!(TypeCmp, T));
380         else
381             alias TypeSet = TypeSet!(NoDuplicates!T);
382     else
383         alias TypeSet = TypeSet!(staticMap!(TryRemoveConst, T));
384 }
385 
386 // IonNull goes first as well
387 private template isIonNull(T)
388 {
389     static if (is(T == TagInfo!(U, name), U, string name))
390         enum isIonNull = .isIonNull!U;
391     else
392         enum isIonNull = T.stringof == "IonNull";
393 }
394 
395 private template TypeCmp(A, B)
396 {
397     enum bool TypeCmp = is(A == B) ? false:
398     is(A == typeof(null)) ? true:
399     is(B == typeof(null)) ? false:
400     isIonNull!A ? true:
401     isIonNull!B ? false:
402     is(A == void) || is(A == TagInfo!(void, vaname), string vaname) ? true:
403     is(B == void) || is(A == TagInfo!(void, vbname), string vbname) ? false:
404     A.sizeof < B.sizeof ? true:
405     A.sizeof > B.sizeof ? false:
406     A.mangleof < B.mangleof;
407 }
408 
409 ///
410 version(mir_core_test) unittest
411 {
412     static struct S {}
413     alias C = S;
414     alias Int = int;
415     static assert(is(TypeSet!(S, int) == TypeSet!(Int, C)));
416     static assert(is(TypeSet!(S, int, int) == TypeSet!(Int, C)));
417     static assert(!is(TypeSet!(uint, S) == TypeSet!(int, S)));
418 }
419 
420 private template applyTags(string[] tagNames, T...)
421     if (tagNames.length == T.length)
422 {
423     import std.meta: AliasSeq;
424     static if (tagNames.length == 0)
425         alias applyTags = AliasSeq!();
426     else
427         alias applyTags =  AliasSeq!(TagInfo!(T[0], tagNames[0]), .applyTags!(tagNames[1 .. $], T[1 .. $]));
428 }
429 
430 /++
431 Checks if the type list is $(LREF TypeSet).
432 +/
433 enum bool isTypeSet(T...) = is(T == TypeSet!T);
434 
435 ///
436 @safe pure version(mir_core_test) unittest
437 {
438     static assert(isTypeSet!(TypeSet!()));
439     static assert(isTypeSet!(TypeSet!void));
440     static assert(isTypeSet!(TypeSet!(void, int, typeof(null))));
441 }
442 
443 /++
444 Variant Type (aka Algebraic Type).
445 
446 The implementation is defined as
447 ----
448 alias Variant(T...) = Algebraic!(TypeSet!T);
449 ----
450 
451 Compatible with BetterC mode.
452 +/
453 alias Variant(T...) = Algebraic!(TypeSet!T);
454 
455 ///
456 @safe pure @nogc
457 version(mir_core_test) unittest
458 {
459     Variant!(int, double, string) v = 5;
460     assert(v.get!int == 5);
461     v = 3.14;
462     assert(v == 3.14);
463     // auto x = v.get!long; // won't compile, type long not allowed
464     // v = '1'; // won't compile, type char not allowed
465 }
466 
467 /// Single argument Variant
468 // and Type with copy constructor
469 @safe pure nothrow @nogc
470 version(mir_core_test) unittest
471 {
472     static struct S
473     {
474         int n;
475         this(ref return scope inout S rhs) inout
476         {
477             this.n = rhs.n + 1;
478         }
479     }
480 
481     Variant!S a = S();
482     auto b = a;
483 
484     import mir.conv;
485     assert(a.get!S.n == 0);
486     assert(b.n == 1); //direct access of a member in case of all algebraic types has this member
487 }
488 
489 /// Empty type set
490 @safe pure nothrow @nogc version(mir_core_test) unittest
491 {
492     Variant!() a;
493     auto b = a;
494     assert(a.toHash == 0);
495     assert(a == b);
496     assert(a <= b && b >= a);
497     static assert(typeof(a).sizeof == 1);
498 }
499 
500 /// Small types
501 @safe pure nothrow @nogc version(mir_core_test) unittest
502 {
503     static struct S { ubyte d; }
504     static assert(Nullable!(byte, char, S).sizeof == 2);
505 }
506 
507 @safe pure nothrow @nogc version(mir_core_test) unittest
508 {
509     static struct S { ubyte[3] d; }
510     static assert(Nullable!(ushort, wchar, S).sizeof == 6);
511 }
512 
513 // /// opPostMove support
514 // @safe pure @nogc nothrow
515 // version(mir_core_test) unittest
516 // {
517 //     import std.algorithm.mutation: move;
518 
519 //     static struct S
520 //     {
521 //         uint s;
522 
523 //         void opPostMove(const ref S old) nothrow
524 //         {
525 //             this.s = old.s + 1;
526 //         }
527 //     }
528 
529 //     Variant!S a;
530 
531 //     auto b = a.move;
532 //     assert(b.s == 1);
533 // }
534 
535 /++
536 Tagged Variant Type (aka Tagged Algebraic Type).
537 
538 Compatible with BetterC mode.
539 
540 Template has two declarations:
541 ----
542 // and
543 template TaggedVariant(T)
544     if (is(T == union))
545 {
546     ...
547 }
548 ----
549 
550 See_also: $(LREF Variant), $(LREF isTaggedVariant).
551 +/
552 deprecated ("Use Algebraic!Union instead")
553 template TaggedVariant(T)
554     if (is(T == union))
555 {
556     alias TaggedVariant = Algebraic!T;
557 }
558 
559 /// Json Value with styles
560 @safe pure
561 version(mir_core_test) unittest
562 {
563     enum Style { block, flow }
564 
565     static struct SomeMetadata {
566         int a;
567         @safe pure nothrow @nogc scope
568         int opCmp(scope const SomeMetadata rhs) const { return a - rhs.a; }
569     }
570 
571     static struct ParsePosition
572     {
573         string file, line, column;
574 
575         void toString()(scope ref W w) scope const
576         {
577             w.put(file);
578             if (line) {
579                 w.put("("); w.put(line);
580                 if (column) { w.put(","); w.put(column); }
581                 w.put(")");
582             }
583         }
584     }
585 
586     static union Json_
587     {
588         typeof(null) null_;
589         bool boolean;
590         long integer;
591         double floating;
592         // Not, that `string` is't builtin type but an alias in `object.d`
593         // So we can use `string` as a name of the string field
594         immutable(char)[] string;
595         This[] array;
596         // commented out to test `opCmp` primitive
597         // This[immutable(char)[]] object;
598 
599     @algMeta:
600         bool active;
601         SomeMetadata metadata;
602     @algTransp:
603         Style style;
604         @algVerbose ParsePosition position;
605     }
606 
607     alias JsonAlgebraic = Algebraic!Json_;
608 
609     // typeof(null) has priority
610     static assert(JsonAlgebraic.Kind.init == JsonAlgebraic.Kind.null_);
611     static assert(JsonAlgebraic.Kind.null_ == 0);
612 
613     // Kind and AllowedTypes has the same order
614     static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.array] == JsonAlgebraic[]));
615     static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.boolean] == bool));
616     static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.floating] == double));
617     static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.integer] == long));
618     static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.null_] == typeof(null)));
619     // static assert (is(JsonAlgebraic.AllowedTypes[JsonAlgebraic.Kind.object] == JsonAlgebraic[string]));
620 
621     JsonAlgebraic v;
622     assert(v.kind == JsonAlgebraic.Kind.null_);
623 
624     v = 1;
625     assert(v.kind == JsonAlgebraic.Kind.integer);
626     assert(v == 1);
627     v = JsonAlgebraic(1);
628     assert(v == 1);
629     v = v.get!(long, double);
630 
631     v = "Tagged!";
632     // member-based access. Simple!
633     assert(v..string == "Tagged!");
634     // type-based access
635     assert(v.get!string == "Tagged!");
636     assert(v.trustedGet!string == "Tagged!");
637 
638     assert(v.kind == JsonAlgebraic.Kind..string);
639 
640     assert(v.get!"string" == "Tagged!"); // string-based get
641     assert(v.trustedGet!"string" == "Tagged!"); // string-based trustedGet
642 
643     assert(v.get!(JsonAlgebraic.Kind..string) == "Tagged!"); // Kind-based get
644     assert(v.trustedGet!(JsonAlgebraic.Kind..string) == "Tagged!"); // Kind-based trustedGet
645 
646     // checks
647     assert(v._is!string); // type based
648     assert(v._is!"string"); // string based
649     assert(v._is!(JsonAlgebraic.Kind..string)); //
650 
651     v = null;
652     assert(v.kind == JsonAlgebraic.Kind.null_);
653 
654     v = [JsonAlgebraic("str"), JsonAlgebraic(4.3)];
655 
656     assert(v.kind == JsonAlgebraic.Kind.array);
657     assert(v.trustedGet!(JsonAlgebraic[])[1].kind == JsonAlgebraic.Kind.floating);
658 
659     JsonAlgebraic w = v;
660     w.style = Style.flow;
661     assert(v.style != w.style);
662     assert(v == w);
663     assert(v <= w);
664     assert(v >= w);
665     assert(v.toHash == w.toHash);
666     w.active = true;
667     assert(v != w);
668     assert(v.toHash != w.toHash);
669     assert(v.get!"array" == w.get!"array");
670     assert(v < w);
671 
672     // test equality with self-referencing allowed type
673     auto arr = [JsonAlgebraic("str"), JsonAlgebraic(120)];
674     v = arr;
675     assert(v == arr);
676     assert(v == [JsonAlgebraic("str"), JsonAlgebraic(120)]);
677 }
678 
679 /// Wrapped algebraic with propogated primitives
680 @safe pure
681 version(mir_core_test) unittest
682 {
683     static struct Response
684     {
685         private union Response_
686         {
687             double float_;
688             immutable(char)[] string;
689             Response[] array;
690             Response[immutable(char)[]] table;
691         }
692 
693         alias ResponseAlgebraic = Algebraic!Response_;
694 
695         ResponseAlgebraic data;
696         alias Tag = ResponseAlgebraic.Kind;
697 
698         // propogates opEquals, opAssign, and other primitives
699         alias data this;
700 
701         static foreach (T; ResponseAlgebraic.AllowedTypes)
702             this(T v) @safe pure nothrow @nogc { data = v; }
703     }
704 
705     Response v = 3.0;
706     assert(v.kind == Response.Tag.float_);
707     v = "str";
708     assert(v == "str");
709 }
710 
711 /++
712 Nullable $(LREF Variant) Type (aka Algebraic Type).
713 
714 The implementation is defined as
715 ----
716 alias Nullable(T...) = Variant!(typeof(null), T);
717 ----
718 
719 In additional to common algebraic API the following members can be accesssed:
720 $(UL
721 $(LI $(LREF .Algebraic.isNull))
722 $(LI $(LREF .Algebraic.nullify))
723 $(LI $(LREF .Algebraic.get.2))
724 )
725 
726 Compatible with BetterC mode.
727 +/
728 alias Nullable(T...) = Variant!(typeof(null), T);
729 
730 /// ditto
731 Nullable!T nullable(T)(T t)
732 {
733     import core.lifetime: forward;
734     return Nullable!T(forward!t);
735 }
736 
737 /++
738 Single type `Nullable`
739 +/
740 @safe pure @nogc
741 version(mir_core_test) unittest
742 {
743     static assert(is(Nullable!int == Variant!(typeof(null), int)));
744 
745     Nullable!int a = 5;
746     assert(a.get!int == 5);
747 
748     a.nullify;
749     assert(a.isNull);
750 
751     a = 4;
752     assert(!a.isNull);
753     assert(a.get == 4);
754     assert(a == 4);
755     a = 4;
756 
757     a = null;
758     assert(a == null);
759 }
760 
761 /// Empty nullable type set support
762 @safe pure nothrow @nogc version(mir_core_test) unittest
763 {
764     Nullable!() a;
765     auto b = a;
766     assert(a.toHash == 0);
767     assert(a == b);
768     assert(a <= b && b >= a);
769     static assert(typeof(a).sizeof == 1);
770 }
771 
772 private bool contains(scope const char[][] names, scope const char[] member)
773 @safe pure nothrow @nogc
774 {
775     foreach (name; names)
776         if (name == member)
777             return true;
778     return false;
779 }
780 
781 /++
782 Algebraic implementation.
783 For more portable code, it is higly recommeded to don't use this template directly.
784 Instead, please use of $(LREF Variant) and $(LREF Nullable), which sort types.
785 +/
786 struct Algebraic(T__...)
787 {
788     import mir.internal.meta: getUDAs;
789     import core.lifetime: moveEmplace;
790     import mir.conv: emplaceRef;
791     import mir.reflection: isPublic, hasField, isProperty;
792     import std.meta: Filter, AliasSeq, ApplyRight, anySatisfy, allSatisfy, staticMap, templateOr, templateNot, templateAnd;
793     import std.traits:
794         hasElaborateAssign,
795         hasElaborateCopyConstructor,
796         hasElaborateDestructor,
797         hasMember,
798         hasUDA,
799         isAggregateType,
800         isAssociativeArray,
801         isDynamicArray,
802         isEqualityComparable,
803         isOrderingComparable,
804         Largest,
805         Unqual
806         ;
807 
808     static if (T__.length == 1 && is(T__[0] == union))
809     {
810         private alias UMTypeInfoOf__(immutable(char)[] member) = TagInfo!(
811             typeof(__traits(getMember, T__[0], member)),
812             member,
813             getUDAs!(T__[0], member, algMeta),
814             getUDAs!(T__[0], member, algTransp),
815             getUDAs!(T__[0], member, algVerbose),
816         );
817 
818         private alias UMGetType__(alias TI) = TI.Type;
819         private enum bool UMGetMeta(alias TI) = TI.meta;
820 
821         private alias AllInfo__ = staticMap!(UMTypeInfoOf__, __traits(allMembers, T__[0]));
822         private alias TypesInfo__ = Filter!(templateNot!UMGetMeta, AllInfo__);
823         private alias MetaInfo__ = Filter!(UMGetMeta, AllInfo__);
824         alias Types__ = staticMap!(UMGetType__, TypesInfo__);
825 
826         /++
827         +/
828         static immutable char[][] metaFieldNames__ = () {
829             immutable(char)[][] ret;
830             foreach (T; MetaInfo__)
831                 ret ~= T.tag;
832             return ret;
833         } ();
834 
835         /++
836         +/
837         static immutable char[][] typeFieldNames__ = () {
838             immutable(char)[][] ret;
839             foreach (T; TypesInfo__)
840                 ret ~= T.tag;
841             return ret;
842         } ();
843     }
844     else
845     {
846         alias Types__ = T__;
847         private alias MetaInfo__ = T__[0 .. 0];
848         enum immutable(char[][]) metaFieldNames__ = null;
849         enum immutable(char[][]) typeFieldNames__ = null;
850     }
851 
852     private enum bool variant_test__ = is(Types__ == AliasSeq!(typeof(null), double));
853 
854     /++
855     Allowed types list
856     See_also: $(LREF TypeSet)
857     +/
858     alias AllowedTypes = AliasSeq!(ReplaceTypeUnless!(.isVariant, .This, Algebraic!T__, Types__));
859 
860     version(mir_core_test)
861     static if (variant_test__)
862     ///
863     unittest
864     {
865         import std.meta: AliasSeq;
866 
867         alias V = Nullable!
868         (
869             This*,
870             string,
871             double,
872             bool,
873         );
874 
875         static assert(is(V.AllowedTypes == TypeSet!(
876             typeof(null),
877             bool,
878             string,
879             double,
880             V*)));
881     }
882 
883     static foreach (i, T; MetaInfo__)
884         mixin ("MetaInfo__["  ~ i.stringof ~ "].Type " ~ T.tag ~";");
885 
886     private alias _Payload = Replace!(void, _Void!(), Replace!(typeof(null), _Null!(), AllowedTypes));
887 
888     private static union Storage__
889     {
890         _Payload payload;
891 
892         static foreach (int i, P; _Payload)
893             mixin(`alias _member_` ~ i.stringof ~ ` = payload[` ~ i.stringof ~ `];`);
894 
895         static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null))) || is(AllowedTypes == AliasSeq!void))
896             ubyte[0] bytes;
897         else
898             ubyte[Largest!_Payload.sizeof] bytes;
899     }
900 
901     private Storage__ storage__;
902 
903     static if (AllowedTypes.length > 1)
904     {
905         static if ((Storage__.alignof & 1) && _Payload.length <= ubyte.max)
906             private alias ID__ = ubyte;
907         else
908         static if ((Storage__.alignof & 2) && _Payload.length <= ushort.max)
909             private alias ID__ = ushort;
910         else
911         // static if (Storage__.alignof & 3)
912             private alias ID__ = uint;
913         // else
914         //     private alias ID__ = ulong;
915 
916         ID__ identifier__;
917     }
918     else
919     {
920         private alias ID__ = uint;
921         enum ID__ identifier__ = 0;
922     }
923 
924     version (D_Ddoc)
925     {
926         /++
927         Algebraic Kind.
928 
929         Defined as enum for tagged algebraics and as unsigned for common algebraics.
930 
931         The Kind enum contains the members defined using tag names.
932 
933         If the algebraic type is $(LREF Nullable) then the default Kind enum member has zero value and corresponds to `typeof(null)`.
934 
935         See_also: $(LREF TaggedVariant).
936         +/
937         enum Kind { _not_me_but_tags_name_list_ }
938     }
939 
940     static if (typeFieldNames__.length)
941     {
942         version (D_Ddoc){}
943         else
944         {
945             mixin(enumKindText(typeFieldNames__));
946 
947         }
948     }
949     else
950     {
951         version (D_Ddoc){}
952         else
953         {
954             alias Kind = ID__;
955         }
956     }
957 
958     /++
959     Returns: $(LREF .Algebraic.Kind).
960 
961     Defined as enum for tagged algebraics and as unsigned for common algebraics.
962     See_also: $(LREF TaggedVariant).
963     +/
964     Kind kind() const @safe pure nothrow @nogc @property
965     {
966         assert(identifier__ <= Kind.max);
967         return cast(Kind) identifier__;
968     }
969 
970     static if (anySatisfy!(hasElaborateDestructor, _Payload))
971     ~this() @trusted
972     {
973         S: switch (identifier__)
974         {
975             static foreach (i, T; AllowedTypes)
976             static if (hasElaborateDestructor!T)
977             {
978                 case i:
979                     (*cast(Unqual!(_Payload[i])*)&storage__.payload[i]).__xdtor;
980                     break S;
981             }
982             default:
983         }
984         version(mir_secure_memory)
985             storage__.bytes = 0xCC;
986     }
987 
988     // static if (anySatisfy!(hasOpPostMove, _Payload))
989     // void opPostMove(const ref typeof(this) old)
990     // {
991     //     S: switch (identifier__)
992     //     {
993     //         static foreach (i, T; AllowedTypes)
994     //         static if (hasOpPostMove!T)
995     //         {
996     //             case i:
997     //                 this.storage__.payload[i].opPostMove(old.storage__.payload[i]);
998     //                 return;
999     //         }
1000     //         default: return;
1001     //     }
1002     // }
1003 
1004     static if (AllowedTypes.length)
1005     {
1006         static if (!__traits(compiles, (){ _Payload[0] arg; }))
1007         {
1008             @disable this();
1009         }
1010 
1011         static if (allSatisfy!(isDynamicArray, AllowedTypes))
1012         {
1013             auto length()() const @property
1014             {
1015                 switch (identifier__)
1016                 {
1017                     static foreach (i, T; AllowedTypes)
1018                     {
1019                         case i:
1020                             return trustedGet!T().length;
1021                     }
1022                     default: assert(0);
1023                 }
1024             }
1025 
1026             auto length()(size_t length) @property
1027             {
1028                 switch (identifier__)
1029                 {
1030                     static foreach (i, T; AllowedTypes)
1031                     {
1032                         case i:
1033                             return trustedGet!T().length = length;
1034                     }
1035                     default: assert(0);
1036                 }
1037             }
1038 
1039             alias opDollar(size_t pos : 0) = length;
1040 
1041             /// Returns: slice type of `Slice!(IotaIterator!size_t)`
1042             size_t[2] opSlice(size_t dimension)(size_t i, size_t j) @safe scope const
1043                 if (dimension == 0)
1044             in(i <= j, "Algebraic.opSlice: the left opSlice boundary must be less than or equal to the right bound.")
1045             {
1046                 return [i, j];
1047             }
1048 
1049             auto opIndex()(size_t index)
1050             {
1051                 return this.visit!(a => a[index]);
1052             }
1053 
1054             auto opIndex()(size_t index) const
1055             {
1056                 return this.visit!(a => a[index]);
1057             }
1058 
1059             auto opIndex()(size_t[2] index)
1060             {
1061                 auto ret = this;
1062                 S: switch (identifier__)
1063                 {
1064                     static foreach (i, T; AllowedTypes)
1065                     {
1066                         case i:
1067                             ret.trustedGet!T() = ret.trustedGet!T()[index[0] .. index[1]];
1068                             break S;
1069                     }
1070                     default: assert(0);
1071                 }
1072                 return ret;
1073             }
1074 
1075             auto opIndexAssign(T)(T value, size_t index)
1076             {
1077                 return this.tryMatch!((ref array, ref value) => array[index] = value)(value);
1078             }
1079         }
1080     }
1081 
1082     /// Construct an algebraic type from its subset.
1083     this(RhsTypes...)(Algebraic!RhsTypes rhs)
1084         if (!(hasElaborateCopyConstructor!(Algebraic!T__) && is(Algebraic!RhsTypes == typeof(this)))  && allSatisfy!(Contains!AllowedTypes, Algebraic!RhsTypes.AllowedTypes))
1085     {
1086         import core.lifetime: move;
1087         static if (is(RhsTypes == Types__))
1088             this = move(rhs);
1089         else
1090         {
1091             static foreach (member; metaFieldNames__)
1092                 static if (Algebraic!RhsTypes.metaFieldNames__.contains(member))
1093                     __traits(getMember, this, member) = move(__traits(getMember, rhs, member));
1094 
1095             switch (rhs.identifier__)
1096             {
1097                 static foreach (i, T; Algebraic!RhsTypes.AllowedTypes)
1098                 {
1099                     case i:
1100                         static if (__traits(compiles, __ctor(move(rhs.trustedGet!T))))
1101                             __ctor(move(rhs.trustedGet!T));
1102                         else
1103                             __ctor(rhs.trustedGet!T);
1104                         return;
1105                 }
1106                 default:
1107                     assert(0, variantMemberExceptionMsg);
1108             }
1109         }
1110     }
1111 
1112     version(mir_core_test)
1113     static if (variant_test__)
1114     ///
1115     unittest
1116     {
1117         alias Float = Variant!(float, double);
1118         alias Int = Variant!(long, int);
1119         alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes);
1120 
1121         Float fp = 3.0;
1122         Number number = fp; // constructor call
1123         assert(number == 3.0);
1124 
1125         Int integer = 12L;
1126         number = Number(integer);
1127         assert(number == 12L);
1128     }
1129 
1130     static if (!allSatisfy!(isCopyable, AllowedTypes))
1131         @disable this(this);
1132     else
1133     static if (anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
1134     {
1135         import std.meta: Filter;
1136         private alias CC_AllowedTypes = Filter!(hasElaborateCopyConstructor, AllowedTypes);
1137         // private enum _allCanImplicitlyRemoveConst = allSatisfy!(canImplicitlyRemoveConst, AllowedTypes);
1138         // private enum _allCanRemoveConst = allSatisfy!(canRemoveConst, AllowedTypes);
1139         // private enum _allHaveImplicitSemiMutableConstruction = _allCanImplicitlyRemoveConst && _allHaveMutableConstruction;
1140 
1141         static if (__VERSION__ < 2094)
1142         private static union _StorageI(uint i)
1143         {
1144             _Payload[i] payload;
1145             ubyte[Storage__.bytes.length] bytes;
1146         }
1147 
1148         static if (allSatisfy!(hasInoutConstruction, CC_AllowedTypes))
1149         this(return ref scope inout Algebraic rhs) inout
1150         {
1151             static foreach (member; metaFieldNames__)
1152                 __traits(getMember, this, member) = __traits(getMember, rhs, member);
1153 
1154             static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__;
1155             static foreach (int i, T; AllowedTypes)
1156             static if (!is(T == typeof(null)) && !is(T == void))
1157             {
1158                 if (identifier__ == i)
1159                 {
1160                     static if (__VERSION__ < 2094)
1161                     {
1162                         storage__.bytes = () inout @trusted {
1163                             auto ret =  inout _StorageI!i(rhs.trustedGet!T);
1164                             return ret.bytes;
1165                         } ();
1166                         return;
1167                     }
1168                     else
1169                     {
1170                         storage__ = () inout @trusted {
1171                             mixin(`inout Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
1172                             return ret;
1173                         } ();
1174                         return;
1175                     }
1176                 }
1177             }
1178         }
1179         else
1180         {
1181             static if (allSatisfy!(hasMutableConstruction, CC_AllowedTypes))
1182             this(return ref scope Algebraic rhs)
1183             {
1184                 static foreach (member; metaFieldNames__)
1185                     __traits(getMember, this, member) = __traits(getMember, rhs, member);
1186 
1187                 static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__;
1188                 static foreach (int i, T; AllowedTypes)
1189                 static if (!is(T == typeof(null)) && !is(T == void))
1190                 {
1191                     if (identifier__ == i)
1192                     {
1193                         storage__ = () {
1194                             mixin(`Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
1195                             return ret;
1196                         } ();
1197                         return;
1198                     }
1199                 }
1200             }
1201 
1202             static if (allSatisfy!(hasConstConstruction, CC_AllowedTypes))
1203             this(return ref scope const Algebraic rhs) const
1204             {
1205                 static foreach (member; metaFieldNames__)
1206                     __traits(getMember, this, member) = __traits(getMember, rhs, member);
1207 
1208                 static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__;
1209                 static foreach (int i, T; AllowedTypes)
1210                 static if (!is(T == typeof(null)) && !is(T == void))
1211                 {
1212                     if (identifier__ == i)
1213                     {
1214                         storage__ = () const {
1215                             mixin(`const Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
1216                             return ret;
1217                         } ();
1218                         return;
1219                     }
1220                 }
1221             }
1222 
1223             static if (allSatisfy!(hasImmutableConstruction, CC_AllowedTypes))
1224             this(return ref scope immutable Algebraic rhs) immutable
1225             {
1226                 static foreach (member; metaFieldNames__)
1227                     __traits(getMember, this, member) = __traits(getMember, rhs, member);
1228 
1229                 static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__;
1230                 static foreach (int i, T; AllowedTypes)
1231                 static if (!is(T == typeof(null)) && !is(T == void))
1232                 {
1233                     if (identifier__ == i)
1234                     {
1235                         storage__ = () immutable {
1236                             mixin(`immutable Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
1237                             return ret;
1238                         } ();
1239                         return;
1240                     }
1241                 }
1242             }
1243 
1244             static if (allSatisfy!(hasSemiImmutableConstruction, CC_AllowedTypes))
1245             this(return ref scope const Algebraic rhs) immutable
1246             {
1247                 static foreach (member; metaFieldNames__)
1248                     __traits(getMember, this, member) = __traits(getMember, rhs, member);
1249 
1250                 static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__;
1251                 static foreach (int i, T; AllowedTypes)
1252                 static if (!is(T == typeof(null)) && !is(T == void))
1253                 {
1254                     if (identifier__ == i)
1255                     {
1256                         storage__ = () const {
1257                             mixin(`immutable Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
1258                             return ret;
1259                         } ();
1260                         return;
1261                     }
1262                 }
1263             }
1264 
1265             static if (allSatisfy!(hasSemiMutableConstruction, CC_AllowedTypes))
1266             this(return ref scope const Algebraic rhs)
1267             {
1268                 static foreach (member; metaFieldNames__)
1269                     __traits(getMember, this, member) = __traits(getMember, rhs, member);
1270 
1271                 static if (AllowedTypes.length > 1) this.identifier__ = rhs.identifier__;
1272                 static foreach (int i, T; AllowedTypes)
1273                 static if (!is(T == typeof(null)) && !is(T == void))
1274                 {
1275                     if (identifier__ == i)
1276                     {
1277                         storage__ = () const {
1278                             mixin(`const Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
1279                             return ret;
1280                         } ();
1281                         return;
1282                     }
1283                 }
1284             }
1285         }
1286     }
1287 
1288     /++
1289     +/
1290     static if (typeFieldNames__.length)
1291     size_t toHash() scope @trusted const pure nothrow @nogc
1292     {
1293         size_t hash;
1294 
1295 
1296         static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null))))
1297         {
1298         }
1299         else{S:
1300         switch (identifier__)
1301         {
1302             import std.traits: isArray;
1303             static foreach (i, T; AllowedTypes)
1304             {
1305                 case i: {
1306                     static if (is(T == void))
1307                         hash = i;
1308                     else
1309                     static if (is(T == typeof(null)))
1310                         hash = i;
1311                     else
1312                     static if (typeFieldNames__.length) // force for tagged types
1313                     {
1314                         static if (__traits(hasMember, T, "toHash"))
1315                             hash = trustedGet!T.toHash;
1316                         else
1317                         static if (isArray!T)
1318                             foreach (ref e; trustedGet!T)
1319                                 static if (__traits(hasMember, typeof(e), "toHash"))
1320                                     hash = hashOf(e.toHash, hash);
1321                                 else
1322                                     hash = hashOf(e, hash);
1323                         else
1324                             hash = hashOf(trustedGet!T);
1325                     }
1326                     else
1327                     static if (__traits(compiles, hashOf(trustedGet!T.hashOf, i ^ hash)))
1328                         hash = hashOf(trustedGet!T.hashOf, i ^ hash);
1329                     else
1330                     {
1331                         debug pragma(msg, "Mir warning: couldn't compute hash. Expected a `size_t toHash() scope @safe const pure nothrow @nogc` method for " ~ T.stringof);
1332                         hash = i;
1333                     }
1334                     break S;
1335                 }
1336             }
1337             default: assert(0);
1338         }}
1339 
1340         static foreach (i, T; MetaInfo__)
1341         static if (!T.transparent)
1342         {
1343             static if (is(MetaFieldsTypes[i] == class) || is(MetaFieldsTypes[i] == interface))
1344             {{
1345                 scope eqfun = delegate() {
1346                     hash = hashOf(__traits(getMember, this, T.tag), hash);
1347                 };
1348                 trustedAllAttr(eqfun)();
1349             }}
1350             else
1351                 hash = hashOf(__traits(getMember, this, T.tag), hash);
1352         }
1353         return hash;
1354     }
1355     else
1356     size_t toHash() const scope @trusted nothrow
1357     {
1358         size_t hash;
1359 
1360 
1361         static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null))))
1362         {
1363         }
1364         else{S:
1365         switch (identifier__)
1366         {
1367             import std.traits: isArray;
1368             static foreach (i, T; AllowedTypes)
1369             {
1370                 case i: {
1371                     static if (is(T == void))
1372                         hash = i;
1373                     else
1374                     static if (is(T == typeof(null)))
1375                         hash = i;
1376                     else
1377                     static if (typeFieldNames__.length) // force for tagged types
1378                     {
1379                         static if (__traits(hasMember, T, "toHash"))
1380                             hash = trustedGet!T.toHash;
1381                         else
1382                         static if (isArray!T)
1383                             foreach (ref e; trustedGet!T)
1384                                 static if (__traits(hasMember, typeof(e), "toHash"))
1385                                     hash = hashOf(e.toHash, hash);
1386                                 else
1387                                     hash = hashOf(e, hash);
1388                         else
1389                             hash = hashOf(trustedGet!T);
1390                     }
1391                     else
1392                     static if (__traits(compiles, hashOf(trustedGet!T.hashOf, i ^ hash)))
1393                         hash = hashOf(trustedGet!T.hashOf, i ^ hash);
1394                     else
1395                     {
1396                         debug pragma(msg, "Mir warning: can't compute hash. Expexted `size_t toHash() scope @safe const pure nothrow @nogc` method for " ~ T.stringof);
1397                         hash = i;
1398                     }
1399                     break S;
1400                 }
1401             }
1402             default: assert(0);
1403         }}
1404 
1405         static foreach (i, T; MetaInfo__)
1406         static if (!T.transparent)
1407         {
1408             static if (is(MetaFieldsTypes[i] == class) || is(MetaFieldsTypes[i] == interface))
1409             {{
1410                 scope eqfun = delegate() {
1411                     hash = hashOf(__traits(getMember, this, T.tag), hash);
1412                 };
1413                 trustedAllAttr(eqfun)();
1414             }}
1415             else
1416                 hash = hashOf(__traits(getMember, this, T.tag), hash);
1417         }
1418         return hash;
1419     }
1420 
1421 
1422     ///
1423     bool opEquals()(scope const Algebraic rhs) scope @trusted const pure nothrow @nogc
1424     {
1425         return opEquals(rhs);
1426     }
1427 
1428     /// ditto
1429     bool opEquals()(scope ref const Algebraic rhs) scope @trusted const pure nothrow @nogc
1430     {
1431         static foreach (i, T; MetaInfo__)
1432         static if (!T.transparent)
1433         {
1434             static if (is(MetaFieldsTypes[i] == class) || is(MetaFieldsTypes[i] == interface))
1435             {{
1436                 scope eqfun = delegate() {
1437                     return __traits(getMember, this, T.tag) != __traits(getMember, rhs, T.tag);
1438                 };
1439                 if (trustedAllAttr(eqfun)())
1440                     return false;
1441             }}
1442             else
1443                 if (__traits(getMember, this, T.tag) != __traits(getMember, rhs, T.tag))
1444                     return false;
1445         }
1446 
1447         static if (AllowedTypes.length == 0)
1448         {
1449             return true;
1450         }
1451         else
1452         {
1453             if (this.identifier__ != rhs.identifier__)
1454                 return false;
1455             switch (identifier__)
1456             {
1457                 static foreach (i, T; AllowedTypes)
1458                 {
1459                     case i:
1460                         static if (is(T == void))
1461                             return rhs._is!void;
1462                         else
1463                         static if (is(T == class) || is(T == interface))
1464                         {{
1465                             scope eqfun = delegate() {
1466                                 return this.trustedGet!T == rhs.trustedGet!T;
1467                             };
1468                             return trustedAllAttr(eqfun)();
1469                         }}
1470                         else
1471                         static if (__traits(isFloating, T))
1472                             return this.trustedGet!T == rhs.trustedGet!T || (this.trustedGet!T != this.trustedGet!T && rhs.trustedGet!T != rhs.trustedGet!T);
1473                         else
1474                             return this.trustedGet!T == rhs.trustedGet!T;
1475                 }
1476                 default: assert(0);
1477             }
1478         }
1479     }
1480 
1481     /++
1482     +/
1483     static if (!anySatisfy!(templateOr!(isAssociativeArray, templateAnd!(isAggregateType, templateNot!hasOpCmp)), staticMap!(basicElementType, AllowedTypes)))
1484     {
1485     static if (typeFieldNames__.length)
1486     int opCmp()(auto ref scope const typeof(this) rhs) scope @trusted const pure nothrow @nogc
1487     {
1488         static foreach (i, T; MetaInfo__)
1489         static if (!T.transparent)
1490         {
1491             static if (__traits(compiles, __cmp(__traits(getMember, this, T.tag), __traits(getMember, rhs, T.tag))))
1492             {
1493                 if (auto d = __cmp(__traits(getMember, this, T.tag), __traits(getMember, rhs, T.tag)))
1494                     return d;
1495             }
1496             else
1497             static if (__traits(hasMember, __traits(getMember, this, T.tag), "opCmp") && !is(MetaFieldsTypes[i] == U*, U))
1498             {
1499                 if (auto d = __traits(getMember, this, T.tag).opCmp(__traits(getMember, rhs, T.tag)))
1500                     return d;
1501             }
1502             else
1503             {
1504                 if (auto d = __traits(getMember, this, T.tag) < __traits(getMember, rhs, T.tag) ? -1 : __traits(getMember, this, T.tag) > __traits(getMember, rhs, T.tag) ? +1 : 0)
1505                     return d;
1506             }
1507         }
1508 
1509 
1510         static if (AllowedTypes.length == 0)
1511         {
1512             return 0;
1513         }
1514         else
1515         {
1516             import std.traits: isArray;
1517             if (auto d = int(this.identifier__) - int(rhs.identifier__))
1518                 return d;
1519             import std.traits: isArray, isPointer;
1520             switch (identifier__)
1521             {
1522                 static foreach (i, T; AllowedTypes)
1523                 {
1524                     case i:
1525                         static if (__traits(hasMember, T, "opCmp") && !isPointer!T)
1526                         {{
1527                             auto ret = this.trustedGet!T.opCmp(rhs.trustedGet!T);
1528                             static if (is(typeof(ret) == int))
1529                                 return ret;
1530                             else
1531                                 return ret < 0 ? -1 : ret > 0 ? 1 : 0;
1532                         }}
1533                         else
1534                         static if (!isArray!T)
1535                             return this.trustedGet!T < rhs.trustedGet!T ? -1 :
1536                                 this.trustedGet!T > rhs.trustedGet!T ? +1 : 0;
1537                         else
1538                             return __cmp(trustedGet!T, rhs.trustedGet!T);
1539                 }
1540                 default: assert(0);
1541             }
1542         }
1543     }
1544     else
1545     int opCmp()(auto ref scope const typeof(this) rhs) scope @trusted const //pure nothrow @nogc
1546     {
1547         static foreach (i, T; MetaInfo__)
1548         static if (!T.transparent)
1549         {
1550             static if (__traits(compiles, __cmp(__traits(getMember, this, T.tag), __traits(getMember, rhs, T.tag))))
1551             {
1552                 if (auto d = __cmp(__traits(getMember, this, T.tag), __traits(getMember, rhs, T.tag)))
1553                     return d;
1554             }
1555             else
1556             static if (__traits(hasMember, __traits(getMember, this, T.tag), "opCmp") && !is(MetaFieldsTypes[i] == U*, U))
1557             {
1558                 if (auto d = __traits(getMember, this, T.tag).opCmp(__traits(getMember, rhs, T.tag)))
1559                     return d;
1560             }
1561             else
1562             {
1563                 if (auto d = __traits(getMember, this, T.tag) < __traits(getMember, rhs, T.tag) ? -1 : __traits(getMember, this, T.tag) > __traits(getMember, rhs, T.tag) ? +1 : 0)
1564                     return d;
1565             }
1566         }
1567 
1568 
1569         static if (AllowedTypes.length == 0)
1570         {
1571             return 0;
1572         }
1573         else
1574         {
1575             import std.traits: isArray;
1576             if (auto d = int(this.identifier__) - int(rhs.identifier__))
1577                 return d;
1578             import std.traits: isArray, isPointer;
1579             switch (identifier__)
1580             {
1581                 static foreach (i, T; AllowedTypes)
1582                 {
1583                     case i:
1584                         static if (__traits(hasMember, T, "opCmp") && !isPointer!T)
1585                         {{
1586                             auto ret = this.trustedGet!T.opCmp(rhs.trustedGet!T);
1587                             static if (is(typeof(ret) == int))
1588                                 return ret;
1589                             else
1590                                 return ret < 0 ? -1 : ret > 0 ? 1 : 0;
1591                         }}
1592                         else
1593                         static if (!isArray!T)
1594                             return this.trustedGet!T < rhs.trustedGet!T ? -1 :
1595                                 this.trustedGet!T > rhs.trustedGet!T ? +1 : 0;
1596                         else
1597                             return __cmp(trustedGet!T, rhs.trustedGet!T);
1598                 }
1599                 default: assert(0);
1600             }
1601         }
1602     }
1603     }
1604 
1605     /// Requires mir-algorithm package
1606     immutable(char)[] toString()() @trusted scope const
1607     {
1608         static if (AllowedTypes.length == 0)
1609         {
1610             return "Algebraic";
1611         }
1612         else
1613         {
1614             import mir.conv: to;
1615             immutable(char)[] ret;
1616             static foreach (i, member; metaFieldNames__)
1617             static if (!MetaInfo__[i].transparent)
1618             {
1619                 static if (__traits(compiles, { auto s = to!(immutable(char)[])(__traits(getMember, this, member));}))
1620                     // should be passed by value to workaround compiler bug
1621                     ret ~= to!(immutable(char)[])(__traits(getMember, this, member));
1622                 else
1623                     ret ~= AllowedTypes[i].stringof;
1624                 ret ~= "::";
1625             }
1626             switch (identifier__)
1627             {
1628                 static foreach (i, T; AllowedTypes)
1629                 {
1630                     case i:
1631                         static if (is(T == void))
1632                             ret ~= "void";
1633                         else
1634                         static if (is(T == typeof(null)))
1635                             ret ~= "null";
1636                         else
1637                         static if (__traits(compiles, { auto s = to!(immutable(char)[])(trustedGet!T);}))
1638                             // should be passed by value to workaround compiler bug
1639                             ret ~= to!(immutable(char)[])(trustedGet!T);
1640                         else
1641                             ret ~= AllowedTypes[i].stringof;
1642                         return ret;
1643                 }
1644                 default: assert(0);
1645             }
1646         }
1647     }
1648 
1649     ///ditto
1650     void toString(W)(ref scope W w) scope const @trusted pure
1651         if (__traits(compiles, ()pure{ w.put("Algebraic"); }))
1652     {
1653         if (false)
1654             return w.put("Algebraic");
1655         static if (AllowedTypes.length == 0)
1656         {
1657             return w.put("Algebraic");
1658         }
1659         else
1660         {
1661             import mir.format: print;
1662             static foreach (i, member; metaFieldNames__)
1663             static if (!MetaInfo__[i].transparent)
1664             {
1665                 static if (__traits(compiles, { import mir.format: print; print(w, __traits(getMember, this, member)); }))
1666                     { import mir.format: print; print(w, __traits(getMember, this, member)); }
1667                 else
1668                     w.put(AllowedTypes[i].stringof);
1669                 w.put("::");
1670             }
1671             switch (identifier__)
1672             {
1673                 static foreach (i, T; AllowedTypes)
1674                 {
1675                     case i:
1676                         static if (is(T == void))
1677                             w.put("void");
1678                         else
1679                         static if (is(T == typeof(null)))
1680                             w.put("null");
1681                         else
1682                         static if (__traits(compiles, { import mir.format: print; print(w, trustedGet!T); }))
1683                             toStringImpl!T(w);
1684                         else
1685                             w.put(AllowedTypes[i].stringof);
1686                         return;
1687                 }
1688                 default: assert(0);
1689             }
1690         }
1691     }
1692 
1693     ///ditto
1694     void toString(W)(ref scope W w) scope const @trusted
1695         if (!__traits(compiles, ()pure{ w.put("Algebraic"); }))
1696     {
1697         if (false)
1698             return w.put("Algebraic");
1699         static if (AllowedTypes.length == 0)
1700         {
1701             return w.put("Algebraic");
1702         }
1703         else
1704         {
1705             switch (identifier__)
1706             {
1707                 static foreach (i, T; AllowedTypes)
1708                 {
1709                     case i:
1710                         static if (is(T == void))
1711                             return w.put("void");
1712                         else
1713                         static if (is(T == typeof(null)))
1714                             return w.put("null");
1715                         else
1716                         static if (__traits(compiles, { import mir.format: print; print(w, trustedGet!T); }))
1717                             return toStringImpl!T(w);
1718                         else
1719                             return w.put(AllowedTypes[i].stringof);
1720                 }
1721                 default: assert(0);
1722             }
1723         }
1724     }
1725 
1726     private void toStringImpl(T, W)(ref scope W w)  @safe scope const pure nothrow @nogc
1727     {
1728         import mir.format: print;
1729         scope pfun = delegate() {
1730             print(w, trustedGet!T);
1731         };
1732         trustedAllAttr(pfun)();
1733     }
1734 
1735     static if (is(AllowedTypes[0] == typeof(null)))
1736     {
1737         ///
1738         bool opCast(C)() const
1739             if (is(C == bool))
1740         {
1741             return identifier__ != 0;
1742         }
1743 
1744         ///
1745         Algebraic opCast(C)() const
1746             if (is(C == Algebraic))
1747         {
1748             return this;
1749         }
1750 
1751         /// Defined if the first type is `typeof(null)`
1752         bool isNull() const @property { return identifier__ == 0; }
1753         /// ditto
1754         void nullify() { this = null; }
1755 
1756         /// ditto
1757         auto get()()
1758             if (allSatisfy!(isCopyable, AllowedTypes[1 .. $]) && AllowedTypes.length != 2 && is(AllowedTypes[0] == typeof(null)))
1759         {
1760             import mir.utility: _expect;
1761             if (_expect(!identifier__, false))
1762             {
1763                 throw variantNullException.toMutable;
1764             }
1765             static if (AllowedTypes.length != 2)
1766             {
1767                 Algebraic!(AllowedTypes[1 .. $]) ret;
1768                 S: switch (identifier__)
1769                 {
1770                     static foreach (i, T; AllowedTypes[1 .. $])
1771                     {
1772                         {
1773                             case i + 1:
1774                                 if (!hasElaborateCopyConstructor!T && !__ctfe)
1775                                     goto default;
1776                                 static if (is(T == void))
1777                                     ret = ret._void;
1778                                 else
1779                                     ret = this.trustedGet!T;
1780                                 break S;
1781                         }
1782                     }
1783                     default:
1784                         ret.storage__.bytes = this.storage__.bytes;
1785                         static if (ret.AllowedTypes.length > 1)
1786                             ret.identifier__ = cast(typeof(ret.identifier__))(this.identifier__ - 1);
1787                 }
1788                 return ret;
1789             }
1790         }
1791 
1792         static if (AllowedTypes.length == 2)
1793         {
1794             /++
1795             Gets the value if not null. If `this` is in the null state, and the optional
1796             parameter `fallback` was provided, it will be returned. Without `fallback`,
1797             calling `get` with a null state is invalid.
1798 
1799             When the fallback type is different from the Nullable type, `get(T)` returns
1800             the common type.
1801 
1802             Params:
1803                 fallback = the value to return in case the `Nullable` is null.
1804 
1805             Returns:
1806                 The value held internally by this `Nullable`.
1807             +/
1808             auto ref inout(AllowedTypes[1]) get() return inout
1809             {
1810                 assert(identifier__, "Called `get' on null Nullable!(" ~ AllowedTypes[1].stringof ~ ").");
1811                 return trustedGet!(AllowedTypes[1]);
1812             }
1813 
1814             version(mir_core_test)
1815             static if (variant_test__)
1816             ///
1817             @safe pure nothrow @nogc
1818             unittest
1819             {
1820                 enum E { a = "a", b = "b" }
1821                 Nullable!E f = E.a;
1822                 auto e = f.get();
1823                 static assert(is(typeof(e) == E), Nullable!E.AllowedTypes.stringof);
1824                 assert(e == E.a);
1825 
1826                 assert(f.get(E.b) == E.a);
1827 
1828                 f = null;
1829                 assert(f.get(E.b) == E.b);
1830             }
1831 
1832             /// ditto
1833             @property auto ref inout(AllowedTypes[1]) get()(auto ref inout(AllowedTypes[1]) fallback) return inout
1834             {
1835                 return isNull ? fallback : get();
1836             }
1837         }
1838     }
1839 
1840     /++
1841     Checks if the underlaying type is an element of a user provided type set.
1842     +/
1843     bool _is(R : Algebraic!RetTypes, RetTypes...)() @safe pure nothrow @nogc const @property
1844         if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes))
1845     {
1846         static if (is(RetTypes == Types__))
1847             return true;
1848         else
1849         {
1850             import std.meta: staticIndexOf;
1851             import std.traits: CopyTypeQualifiers;
1852             alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes;
1853             alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes);
1854             // uint rhsTypeId;
1855             switch (identifier__)
1856             {
1857                 foreach (i, T; AllowedTypes)
1858                 static if (staticIndexOf!(T, RhsAllowedTypes) >= 0)
1859                 {
1860                     case i:
1861                         return true;
1862                 }
1863                 default:
1864                     return false;
1865             }
1866         }
1867     }
1868 
1869     /// ditto
1870     bool _is(RetTypes...)() @safe pure nothrow @nogc const @property
1871         if (RetTypes.length > 1)
1872     {
1873         return this._is!(Variant!RetTypes);
1874     }
1875 
1876     /++
1877     `nothrow` $(LREF .Algebraic.get) alternative that returns an algebraic subset.
1878     +/
1879     auto ref trustedGet(R : Algebraic!RetTypes, this This, RetTypes...)() return @property
1880         if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes))
1881     {
1882         static if (is(RetTypes == Types__))
1883             return this;
1884         else
1885         {
1886             import std.meta: staticIndexOf;
1887             import std.traits: CopyTypeQualifiers;
1888             alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes;
1889             alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes);
1890             // uint rhsTypeId;
1891             switch (identifier__)
1892             {
1893                 foreach (i, T; AllowedTypes)
1894                 static if (staticIndexOf!(T, RhsAllowedTypes) >= 0)
1895                 {
1896                     case i:
1897                         static if (is(T == void))
1898                             return (()@trusted => cast(Ret) Ret._void)();
1899                         else
1900                             return Ret(trustedGet!T);
1901                 }
1902                 default:
1903                     assert(0, variantMemberExceptionMsg);
1904             }
1905         }
1906     }
1907 
1908     /// ditto
1909     template trustedGet(RetTypes...)
1910         if (RetTypes.length > 1)
1911     {
1912         ///
1913         auto ref trustedGet(this This)() return
1914         {
1915             return this.trustedGet!(Variant!RetTypes);
1916         }
1917     }
1918 
1919     version(mir_core_test)
1920     static if (variant_test__)
1921     ///
1922     @safe pure nothrow @nogc
1923     unittest
1924     {
1925         alias Float = Variant!(float, double);
1926         alias Int = Variant!(long, int);
1927         alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes);
1928 
1929         Number number = 3.0;
1930         assert(number._is!Float);
1931         auto fp = number.trustedGet!Float;
1932         static assert(is(typeof(fp) == Float));
1933         assert(fp == 3.0);
1934 
1935         // type list overload
1936         number = 12L;
1937         assert(number._is!(int, long));
1938         auto integer = number.trustedGet!(int, long);
1939         static assert(is(typeof(integer) == Int));
1940         assert(integer == 12L);
1941     }
1942 
1943     static if (typeFieldNames__.length)
1944     {
1945         /// `trustedGet` overload that accept $(LREF .Algebraic.Kind).
1946         alias trustedGet(Kind kind) = trustedGet!(AllowedTypes[kind]);
1947         /// ditto
1948         alias trustedGet(immutable(char)[] kind) = trustedGet!(__traits(getMember, Kind, kind));
1949     }
1950 
1951     /++
1952     Gets an algebraic subset.
1953 
1954     Throws: Exception if the storage contains value of the type that isn't represented in the allowed type set of the requested algebraic.
1955     +/
1956     auto ref get(R : Algebraic!RetTypes, this This, RetTypes...)() return @property
1957         if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes))
1958     {
1959         static if (is(RetTypes == Types__))
1960             return this;
1961         else
1962         {
1963             import std.meta: staticIndexOf;
1964             import std.traits: CopyTypeQualifiers;
1965             alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes;
1966             alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes);
1967             // uint rhsTypeId;
1968             switch (identifier__)
1969             {
1970                 foreach (i, T; AllowedTypes)
1971                 static if (staticIndexOf!(T, RhsAllowedTypes) >= 0)
1972                 {
1973                     case i:
1974                         static if (is(T == void))
1975                             return (()@trusted => cast(Ret) Ret._void)();
1976                         else
1977                             return Ret(trustedGet!T);
1978                 }
1979                 default:
1980                     throw variantMemberException.toMutable;
1981             }
1982         }
1983     }
1984 
1985     /// ditto
1986     template get(RetTypes...)
1987         if (RetTypes.length > 1)
1988     {
1989         ///
1990         auto ref get(this This)() return
1991         {
1992             return this.get!(Variant!RetTypes);
1993         }
1994     }
1995 
1996     version(mir_core_test)
1997     static if (variant_test__)
1998     ///
1999     @safe pure @nogc
2000     unittest
2001     {
2002         alias Float = Variant!(float, double);
2003         alias Int = Variant!(long, int);
2004         alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes);
2005 
2006         Number number = 3.0;
2007         auto fp = number.get!Float;
2008         static assert(is(typeof(fp) == Float));
2009         assert(fp == 3.0);
2010 
2011         // type list overload
2012         number = 12L;
2013         auto integer = number.get!(int, long);
2014         static assert(is(typeof(integer) == Int));
2015         assert(integer == 12L);
2016     }
2017 
2018     static if (typeFieldNames__.length)
2019     {
2020         /// `get` overload that accept $(LREF .Algebraic.Kind).
2021         alias get(Kind kind) = get!(AllowedTypes[kind]);
2022         /// ditto
2023         alias get(immutable(char)[] kind) = get!(__traits(getMember, Kind, kind));
2024 
2025         /// `_is` overload that accept $(LREF .Algebraic.Kind).
2026         alias _is(Kind kind) = _is!(AllowedTypes[kind]);
2027         /// ditto
2028         alias _is(immutable(char)[] kind) = _is!(__traits(getMember, Kind, kind));
2029 
2030         static foreach (member; typeFieldNames__)
2031             mixin ("alias " ~ member ~ `() = get!"` ~ member ~ `";`);
2032     }
2033 
2034     private alias _ReflectionTypes = AllowedTypes[is(AllowedTypes[0] == typeof(null)) .. $];
2035 
2036     static if (_ReflectionTypes.length)
2037     this(this This, Args...)(auto ref Args args)
2038         if (Args.length && (Args.length > 1 || !isLikeVariant!(Args[0])))
2039     {
2040         import std.traits: CopyTypeQualifiers;
2041         import core.lifetime: forward;
2042 
2043         template CanCompile(T)
2044         {
2045             alias Q = CopyTypeQualifiers!(This, T);
2046             enum CanCompile = __traits(compiles, new Q(forward!args));
2047         }
2048 
2049         alias TargetType = Filter!(CanCompile, _ReflectionTypes);
2050         static if (TargetType.length == 0)
2051             static assert(0, typeof(this).stringof ~ ".this: no types can be constructed with arguments " ~ Args.stringof);
2052         static assert(TargetType.length == 1, typeof(this).stringof ~ ".this: multiple types " ~ TargetType.stringof ~ " can be constructed with arguments " ~ Args.stringof);
2053         alias TT = TargetType[0];
2054         static if (is(TT == struct) || is(TT == union))
2055             this(CopyTypeQualifiers!(This, TT)(forward!args));
2056         else
2057             this(new CopyTypeQualifiers!(This, TT)(forward!args));
2058     }
2059 
2060     static if (_ReflectionTypes.length && allSatisfy!(isSimpleAggregateType, _ReflectionTypes))
2061     {
2062         static foreach (member; AllMembersRec!(_ReflectionTypes[0]))
2063         static if (
2064             !.algebraicMembers.contains(member) &&
2065             !metaFieldNames__.contains(member) &&
2066             !typeFieldNames__.contains(member) &&
2067             !(member.length >= 2 && (member[0 .. 2] == "__" || member[$ - 2 .. $] == "__")))
2068         static if (allSatisfy!(ApplyRight!(hasMember, member), _ReflectionTypes))
2069         static if (!anySatisfy!(ApplyRight!(isMemberType, member), _ReflectionTypes))
2070         static if (allSatisfy!(ApplyRight!(isSingleMember, member), _ReflectionTypes))
2071         static if (allSatisfy!(ApplyRight!(isPublic, member), _ReflectionTypes))
2072         {
2073             static if (allSatisfy!(ApplyRight!(hasField, member), _ReflectionTypes) && NoDuplicates!(staticMap!(ApplyRight!(memberTypeOf, member), _ReflectionTypes)).length == 1)
2074             {
2075                 mixin(`ref ` ~ member ~q{()() inout return @trusted pure nothrow @nogc @property { return this.getMember!member; }});
2076             }
2077             else
2078             static if (allSatisfy!(ApplyRight!(templateOr!(hasField, isProperty), member), _ReflectionTypes))
2079             {
2080                 mixin(`auto ref ` ~ member ~q{(this This, Args...)(auto ref Args args) @property { static if (args.length) { import core.lifetime: forward; return this.getMember!member = forward!args; } else return this.getMember!member;  }});
2081             }
2082             static if (allSatisfy!(ApplyRight!(templateNot!(templateOr!(hasField, isProperty)), member), _ReflectionTypes))
2083             {
2084                 mixin(`template ` ~ member ~`(TArgs...) { auto ref ` ~ member ~q{(this This, Args...)(auto ref Args args) { static if (args.length) { import core.lifetime: forward; return this.getMember!(member, TArgs)(forward!args); } else return this.getMember!(member, TArgs);  }} ~ `}`);
2085             }
2086         }
2087     }
2088 
2089     ///
2090     ref opAssign(RhsTypes...)(Algebraic!RhsTypes rhs) return @trusted
2091         if (allSatisfy!(Contains!AllowedTypes, Algebraic!RhsTypes.AllowedTypes) && !is(Algebraic == Algebraic!RhsTypes))
2092     {
2093         import core.lifetime: forward;
2094         this = this.init;
2095         __ctor(forward!rhs);
2096         return this;
2097     }
2098 
2099     // pragma(msg, AllowedTypes);
2100 
2101     static foreach (int i, T; AllowedTypes)
2102     {
2103         /// Zero cost always nothrow `get` alternative
2104         auto ref trustedGet(E)() @trusted @property return inout nothrow
2105             if (is(E == T))
2106         {
2107             assert (i == identifier__);
2108             static if (is(T == typeof(null)))
2109                 return null;
2110             else
2111             static if (is(T == void))
2112                 return;
2113             else
2114                 return storage__.payload[i];
2115         }
2116 
2117         /++
2118         Throws: Exception if the storage contains value of other type
2119         +/
2120         auto ref get(E)() @property return inout
2121             if (is(E == T))
2122         {
2123             import mir.utility: _expect;
2124             static if (AllowedTypes.length > 1)
2125             {
2126                 if (_expect(i != identifier__, false))
2127                 {
2128                     throw variantException.toMutable;
2129                 }
2130             }
2131             return trustedGet!T;
2132         }
2133 
2134         /++
2135         Checks if the storage stores an allowed type.
2136         +/
2137         bool _is(E)() const @property nothrow @nogc
2138             if (is(E == T))
2139         {
2140             return identifier__ == i;
2141         }
2142 
2143         static if (is(T == void))
2144         {
2145             /// Defined if `AllowedTypes` contains `void`
2146             static Algebraic _void()
2147             {
2148                 Algebraic ret;
2149                 ret.storage__ = () {
2150                     import core.lifetime: forward;
2151                     mixin(`Storage__ ret = { _member_` ~ i.stringof ~ ` : _Void!().init };`);
2152                     return ret;
2153                 } ();
2154                 ret.identifier__ = i;
2155                 return ret;
2156             }
2157         }
2158         else
2159         {
2160             ///
2161             static if (isCopyable!(const T) || is(Unqual!T == T))
2162             this(T value)
2163             {
2164                 import core.lifetime: forward;
2165                 static if (is(T == typeof(null)))
2166                     auto rhs = _Null!()();
2167                 else
2168                     alias rhs = forward!value;
2169 
2170                 static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
2171                 {
2172                     storage__.bytes = () @trusted {
2173                         auto ret =  _StorageI!i(rhs);
2174                         return ret.bytes;
2175                     } ();
2176                 }
2177                 else
2178                 {
2179                     storage__ = () {
2180                         mixin(`Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs };`);
2181                         return ret;
2182                     } ();
2183                 }
2184                 static if (_Payload.length > 1)
2185                     identifier__ = i;
2186             }
2187 
2188             /// ditto
2189             static if (isCopyable!(const T))
2190             this(const T value) const
2191             {
2192                 static if (is(T == typeof(null)))
2193                     auto rhs = _Null!()();
2194                 else
2195                     alias rhs = value;
2196                 static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
2197                 {
2198                     storage__.bytes = () const @trusted {
2199                         auto ret =  const _StorageI!i(rhs);
2200                         return ret.bytes;
2201                     } ();
2202                 }
2203                 else
2204                 {
2205                     storage__ = () {
2206                         mixin(`const Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs };`);
2207                         return ret;
2208                     } ();
2209                 }
2210                 static if (_Payload.length > 1)
2211                     identifier__ = i;
2212             }
2213 
2214             /// ditto
2215             static if (isCopyable!(immutable T))
2216             this(immutable T value) immutable
2217             {
2218                 static if (is(T == typeof(null)))
2219                     auto rhs = _Null!()();
2220                 else
2221                     alias rhs = value;
2222                 static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
2223                 {
2224                     storage__.bytes = () const @trusted {
2225                         auto ret = immutable  _StorageI!i(rhs);
2226                         return ret.bytes;
2227                     } ();
2228                 }
2229                 else
2230                 {
2231                     storage__ = () {
2232                         mixin(`immutable Storage__ ret = { _member_` ~ i.stringof ~ ` : rhs };`);
2233                         return ret;
2234                     } ();
2235                 }
2236                 static if (_Payload.length > 1)
2237                     identifier__ = i;
2238             }
2239 
2240             static if (__traits(compiles, (ref T a, ref T b) { moveEmplace(a, b); }))
2241             ///
2242             ref opAssign(T rhs) return @trusted
2243             {
2244                 static foreach (T; MetaInfo__)
2245                     __traits(getMember, this, T.tag) = T.Type.init;
2246 
2247                 import core.lifetime: forward;
2248                 this = this.init;
2249                 __ctor(forward!rhs);
2250                 return this;
2251             }
2252 
2253             /++
2254             +/
2255             bool opEquals()(scope ref const UnqualRec!T rhs) scope @trusted const //pure nothrow @nogc
2256             {
2257                 static if (AllowedTypes.length > 1)
2258                     if (identifier__ != i)
2259                         return false;
2260                 return trustedGet!T == rhs;
2261             }
2262 
2263             ///ditto
2264             bool opEquals()(scope const UnqualRec!T rhs) scope @trusted const //pure nothrow @nogc
2265             {
2266                 return opEquals(rhs);
2267             }
2268 
2269             /++
2270             +/
2271             auto opCmp()(auto ref scope const UnqualRec!T rhs) scope @trusted const pure nothrow @nogc
2272             {
2273                 static if (AllowedTypes.length > 1)
2274                     if (auto d = int(identifier__) - int(i))
2275                         return d;
2276                 static if (__traits(compiles, __cmp(trustedGet!T, rhs)))
2277                     return __cmp(trustedGet!T, rhs);
2278                 else
2279                 static if (__traits(hasMember, T, "opCmp") && !is(T == U*, U))
2280                     return trustedGet!T.opCmp(rhs);
2281                 else
2282                     return trustedGet!T < rhs ? -1 : trustedGet!T > rhs ? +1 : 0;
2283             }
2284 
2285             static if (is(Unqual!T == bool))
2286             {
2287                 private alias contains = Contains!AllowedTypes;
2288                 static if (contains!long && !contains!int)
2289                 {
2290                     this(int value)
2291                     {
2292                         this(long(value));
2293                     }
2294 
2295                     this(int value) const
2296                     {
2297                         this(long(value));
2298                     }
2299 
2300                     this(int value) immutable
2301                     {
2302                         this(long(value));
2303                     }
2304 
2305                     ref opAssign(int rhs) return @trusted
2306                     {
2307                         return opAssign(long(rhs));
2308                     }
2309 
2310                     auto opEquals()(int rhs) const
2311                     {
2312                         return opEquals(long(rhs));
2313                     }
2314 
2315                     auto opCmp()(int rhs) const
2316                     {
2317                         return opCmp(long(rhs));
2318                     }
2319                 }
2320 
2321                 static if (contains!ulong && !contains!uint)
2322                 {
2323                     this(uint value)
2324                     {
2325                         this(ulong(value));
2326                     }
2327 
2328                     this(uint value) const
2329                     {
2330                         this(ulong(value));
2331                     }
2332 
2333                     this(uint value) immutable
2334                     {
2335                         this(ulong(value));
2336                     }
2337 
2338                     ref opAssign(uint rhs) return @trusted
2339                     {
2340                         return opAssign(ulong(rhs));
2341                     }
2342 
2343                     auto opEquals()(uint rhs) const
2344                     {
2345                         return opEquals(ulong(rhs));
2346                     }
2347 
2348                     auto opCmp()(uint rhs) const
2349                     {
2350                         return opCmp(ulong(rhs));
2351                     }
2352                 }
2353             }
2354         }
2355     }
2356 
2357     static if (anySatisfy!(isErr, AllowedTypes))
2358     {
2359         /++
2360         Determines if the variant holds value of some none-$(LREF isVariant) type.
2361         The property is avaliable only for $(ResultVariant)
2362         +/
2363         bool isOk() @safe pure nothrow @nogc const @property
2364         {
2365             switch (identifier__)
2366             {
2367                 static foreach (i, T; AllowedTypes)
2368                 {
2369                     case i:
2370                         return !.isErr!T;
2371                 }
2372                 default: assert(0);
2373             }
2374         }
2375     }
2376 }
2377 
2378 /++
2379 Constructor and methods propagation.
2380 +/
2381 version(mir_core_test)
2382 unittest
2383 {
2384     static struct Base
2385     {
2386         double d;
2387     }
2388 
2389     static class Cc
2390     {
2391         // alias this members are supported
2392         Base base;
2393         alias base this;
2394 
2395         int a;
2396         private string _b;
2397 
2398     @safe pure nothrow @nogc:
2399 
2400         override size_t toHash() scope const { return hashOf(_b) ^ a; }
2401 
2402         string b() const @property { return _b; }
2403         void b(string b) @property { _b = b; }
2404 
2405         int retArg(int v) { return v; }
2406         string retArgT(TArgs...)(int v) { return TArgs.stringof; }
2407 
2408         this(int a, string b)
2409         {
2410             this.a = a;
2411             this._b = b;
2412         }
2413     }
2414 
2415     static struct S
2416     {
2417         string b;
2418         int a;
2419 
2420         double retArg(double v) { return v; }
2421         double retArgT(TArgs...)(int v) { return v * TArgs.length; }
2422 
2423         // alias this members are supported
2424         Base base;
2425         alias base this;
2426     }
2427 
2428     static void inc(ref int a) { a++; }
2429 
2430     alias V = Nullable!(Cc, S); // or Variant!
2431 
2432     auto v = V(2, "str");
2433     assert(v._is!Cc);
2434     assert(v.a == 2);
2435     assert(v.b == "str");
2436     // members are returned by reference if possible
2437     inc(v.a);
2438     assert(v.a == 3);
2439     v.b = "s";
2440     assert(v.b == "s");
2441     // alias this members are supported
2442     v.d = 10;
2443     assert(v.d == 10);
2444     // method call support
2445     assert(v.retArg(100)._is!int);
2446     assert(v.retArg(100) == 100);
2447 
2448     // method with template args support
2449     assert(v.retArgT!dchar(100)._is!string);
2450     assert(v.retArgT!dchar(100) == "(dchar)");
2451 
2452     v = V("S", 5);
2453     assert(v._is!S);
2454     assert(v.a == 5);
2455     assert(v.b == "S");
2456     // members are returned by reference if possible
2457     inc(v.a);
2458     assert(v.a == 6);
2459     v.b = "s";
2460     assert(v.b == "s");
2461     // alias this members are supported
2462     v.d = 15;
2463     assert(v.d == 15);
2464     // method call support
2465     assert(v.retArg(300)._is!double);
2466     assert(v.retArg(300) == 300.0);
2467 }
2468 
2469 // test CTFE
2470 unittest
2471 {
2472     static struct S { string s;}
2473     alias V = Nullable!(double, S);
2474     enum a = V(1.9);
2475     static assert (a == 1.9);
2476     enum b = V(S("str"));
2477     static assert(b == S("str"));
2478     static auto foo(int r)
2479     {
2480         auto s = V(S("str"));
2481         s = r;
2482         return s;
2483     }
2484 
2485     static assert(foo(3) == 3);
2486     static auto bar(int r)
2487     {
2488         auto s = V(S("str"));
2489         s = r;
2490         return s.visit!((double d) => d, (_)=> 0.0)();
2491     }
2492     assert(bar(3) == 3);
2493     static assert(bar(3) == 3);
2494 
2495     static auto bar3(int r)
2496     {
2497         auto s = V(S("str"));
2498         s = r;
2499         return s.match!((double d) => d, (_)=> "3")();
2500     }
2501     assert(bar(3) == 3);
2502     static assert(bar(3) == 3);
2503 }
2504 
2505 @safe pure @nogc nothrow
2506 version(mir_core_test) unittest
2507 {
2508     import core.stdc.string: memcmp;
2509 
2510     static struct C(ubyte payloadSize, bool isPOD, bool hasToHash = true, bool hasOpEquals = true)
2511     {
2512         ubyte[payloadSize] _payload;
2513 
2514     const:
2515 
2516         static if (!isPOD)
2517         {
2518             this(this) {}
2519             ~this() {}
2520         }
2521 
2522     @safe pure nothrow @nogc:
2523 
2524 
2525     static if (hasToHash)
2526         size_t toHash() scope { return hashOf(_payload); }
2527 
2528     static if (hasOpEquals)
2529         auto opEquals(ref const scope  typeof(this) rhs) scope   { return _payload == rhs._payload; }
2530         auto opCmp(ref const scope typeof(this) rhs) @trusted scope  { return memcmp(_payload.ptr, rhs._payload.ptr, _payload.length); }
2531     }
2532 
2533     static foreach (size1; [1, 2, 4, 8, 10, 16, 20])
2534     static foreach (size2; [1, 2, 4, 8, 10, 16, 20])
2535     static if (size1 != size2)
2536     static foreach (isPOD; [true, false])
2537     static foreach (hasToHash; [true, false])
2538     static foreach (hasOpEquals; [true, false])
2539     {{
2540         alias T = Variant!(
2541                 double,
2542                 C!(size1, isPOD, hasToHash, hasOpEquals),
2543                 C!(size2, isPOD, hasToHash, hasOpEquals));
2544         // static assert (__traits(compiles, T.init <= T.init));
2545     }}
2546 }
2547 
2548 // const propogation
2549 @safe pure nothrow @nogc
2550 version(mir_core_test) unittest
2551 {
2552     static struct S1 { immutable(ubyte)* value; }
2553     static struct C1 { immutable(uint)* value; }
2554 
2555     alias V = Variant!(S1, C1);
2556     const V v = S1();
2557     assert(v._is!S1);
2558     V w = v;
2559     w = v;
2560 
2561     immutable f = V(S1());
2562     auto t = immutable V(S1());
2563     // auto j = immutable V(t);
2564     // auto i = const V(t);
2565 }
2566 
2567 // ditto
2568 @safe pure nothrow @nogc
2569 version(mir_core_test) unittest
2570 {
2571     static struct S2 {
2572         uint* value;
2573         this(return ref scope const typeof(this) rhs) {}
2574         ref opAssign(typeof(this) rhs) return { return this; }
2575     }
2576     static struct C2 { const(uint)* value; }
2577 
2578     alias V = Variant!(S2, C2);
2579     const V v = S2();
2580     V w = v;
2581     w = S2();
2582     w = v;
2583     w = cast(const) V.init;
2584 
2585     const f = V(S2());
2586     auto t = const V(f);
2587 }
2588 
2589 @safe pure nothrow @nogc
2590 version(mir_core_test) unittest
2591 {
2592     static struct S3 {
2593         uint* value;
2594         this(return ref scope typeof(this) rhs) {}
2595         this(return ref scope const typeof(this) rhs) const {}
2596         this(return ref scope immutable typeof(this) rhs) immutable {}
2597     }
2598     static struct C3 { immutable(uint)* value; }
2599 
2600     S3 s;
2601     S3 r = s;
2602     r = s;
2603     r = S3.init;
2604 
2605     alias V = Variant!(S3, C3);
2606     V v = S3();
2607     V w = v;
2608     w = S3();
2609     w = V.init;
2610     w = v;
2611 
2612     immutable V e = S3();
2613     auto t = immutable V(S3());
2614     auto j = const V(t);
2615     auto h = t;
2616 
2617     immutable V l = C3();
2618     auto g = immutable V(C3());
2619 }
2620 
2621 @safe pure nothrow @nogc
2622 version(mir_core_test) unittest
2623 {
2624     static struct S4 {
2625         uint* value;
2626         this(return ref scope const typeof(this) rhs) pure immutable {}
2627     }
2628     static struct C4 { immutable(uint)* value; }
2629 
2630 
2631     S4 s;
2632     S4 r = s;
2633     r = s;
2634     r = S4.init;
2635 
2636     alias V = Variant!(S4, C4);
2637     V v = S4();
2638     V w = v;
2639     w = S4();
2640     w = V.init;
2641     w = v;
2642 
2643     {
2644         const V e = S4();
2645         const k = w;
2646         auto t = const V(k);
2647         auto j = immutable V(k);
2648     }
2649 
2650     immutable V e = S4();
2651     immutable k = w;
2652     auto t = immutable V(S4());
2653     auto j = const V(t);
2654     auto h = t;
2655 
2656     immutable V l = C4();
2657     import core.lifetime;
2658     auto g = immutable V(C4());
2659     immutable b = immutable V(s);
2660 }
2661 
2662 @safe pure nothrow @nogc
2663 version(mir_core_test) unittest
2664 {
2665     import core.lifetime: move;
2666 
2667     static struct S5 {
2668         immutable(uint)* value;
2669         this(return ref scope typeof(this) rhs) {}
2670         this(return ref scope const typeof(this) rhs) immutable {}
2671     }
2672     static struct C5 { immutable(uint)* value; }
2673 
2674     S5 s;
2675     S5 r = s;
2676     r = s;
2677     r = S5.init;
2678 
2679     alias V = Variant!(S5, C5);
2680     V v = S5();
2681     V w = v;
2682     w = S5();
2683     w = V.init;
2684     w = v;
2685 
2686     immutable V e = S5();
2687     immutable f = V(S5());
2688     immutable k = w;
2689     auto t = immutable V(S5());
2690     auto j = const V(t);
2691     auto h = t;
2692 
2693     immutable V l = C5();
2694     import core.lifetime;
2695     immutable n = w.move;
2696     auto g = immutable V(C5());
2697     immutable b = immutable V(s);
2698 }
2699 
2700 @safe pure nothrow @nogc
2701 version(mir_core_test) unittest
2702 {
2703     static struct S {
2704         uint* value;
2705         this(this) @safe pure nothrow @nogc {}
2706         // void opAssign(typeof(this) rhs) {}
2707     }
2708     static struct C1 { const(uint)* value; }
2709 
2710     S s;
2711     S r = s;
2712     r = s;
2713     r = S.init;
2714 
2715     alias V = Variant!(S, C1);
2716     V v = S();
2717     V w = v;
2718     w = S();
2719     w = V.init;
2720     w = v;
2721 }
2722 
2723 /++
2724 Applies a delegate or function to the given Variant depending on the held type,
2725 ensuring that all types are handled by the visiting functions.
2726 +/
2727 alias visit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.compileTime, false);
2728 
2729 ///
2730 @safe pure @nogc nothrow
2731 version(mir_core_test) unittest
2732 {
2733     alias Number = Variant!(int, double);
2734 
2735     Number x = 23;
2736     Number y = 1.0;
2737 
2738     assert(x.visit!((int v) => true, (float v) => false));
2739     assert(y.visit!((int v) => false, (float v) => true));
2740 }
2741 
2742 ///
2743 @safe pure @nogc
2744 version(mir_core_test) unittest
2745 {
2746     alias Number = Nullable!(int, double);
2747 
2748     Number z = null; // default
2749     Number x = 23;
2750     Number y = 1.0;
2751 
2752     () nothrow {
2753         assert(x.visit!((int v) => true, (float v) => false));
2754         assert(y.visit!((int v) => false, (v) => true));
2755         assert(z.visit!((typeof(null) v) => true, (v) => false));
2756     } ();
2757 
2758     auto xx = x.get;
2759     static assert (is(typeof(xx) == Variant!(int, double)));
2760     assert(xx.visit!((int v) => v, (float v) => 0) == 23);
2761     assert(xx.visit!((ref v) => v) == 23);
2762 
2763     x = null;
2764     y.nullify;
2765 
2766     assert(x.isNull);
2767     assert(y.isNull);
2768     assert(z.isNull);
2769     assert(z == y);
2770 }
2771 
2772 /// Array primitives propagation
2773 @safe pure version(mir_core_test) unittest
2774 {
2775     Variant!(long[], double[]) array;
2776     array = new long[3];
2777     array[2] = 100;
2778     assert(array == [0L, 0, 100]);
2779     assert(array.length == 3);
2780     assert(array[2] == 100);
2781     array.length = 4;
2782     assert(array == [0L, 0, 100, 0]);
2783     array = array[2 .. 3];
2784     assert(array.length == 1);
2785     assert(array[0] == 100);
2786     array[0] = 10.Variant!(long, double);
2787     assert(array[0] == 10);
2788 }
2789 
2790 /++
2791 Checks $(LREF .Algebraic.toString) and `void`
2792 $(LREF Algerbraic)`.toString` requries `mir-algorithm` package
2793 +/
2794 @safe pure nothrow version(mir_core_test) unittest
2795 {
2796     import mir.conv: to;
2797     enum MIR_ALGORITHM = __traits(compiles, { import mir.format; });
2798 
2799     alias visitorHandler = visit!(
2800         (typeof(null)) => "NULL",
2801         () => "VOID",
2802         (ref r) {r += 1;}, // returns void
2803     );
2804 
2805     alias secondOrderVisitorHandler = visit!(
2806         () => "SO VOID", // void => to "RV VOID"
2807         (str) => str, // string to => it self
2808     );
2809 
2810     alias V = Nullable!(void, int);
2811     static assert(is(V == Variant!(typeof(null), void, int)));
2812 
2813     V variant;
2814 
2815     assert(secondOrderVisitorHandler(visitorHandler(variant)) == "NULL");
2816     assert(variant.toString == "null");
2817 
2818     variant = V._void;
2819     assert(variant._is!void);
2820     assert(is(typeof(variant.get!void()) == void));
2821 
2822     assert(secondOrderVisitorHandler(visitorHandler(variant)) == "VOID");
2823     assert(variant.toString == "void");
2824 
2825     variant = 5;
2826 
2827     assert(secondOrderVisitorHandler(visitorHandler(variant)) == "SO VOID");
2828     assert(variant == 6);
2829     assert(variant.toString == (MIR_ALGORITHM ? "6" : "int"));
2830 }
2831 
2832 version(mir_core_test)
2833 unittest
2834 {
2835     Nullable!() value;
2836     alias visitHandler = visit!((typeof(null)) => null, err);
2837     auto d = visitHandler(value);
2838     assert(d == value);
2839 }
2840 
2841 /++
2842 Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions.
2843 Throws: Exception if `naryFun!visitors` can't be called with provided arguments
2844 +/
2845 alias tryVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.exception, false);
2846 
2847 ///
2848 @safe pure @nogc
2849 version(mir_core_test) unittest
2850 {
2851     alias Number = Variant!(int, double);
2852 
2853     Number x = 23;
2854 
2855     assert(x.tryVisit!((int v) => true));
2856 }
2857 
2858 /++
2859 Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions.
2860 Returns: nullable variant, null value is used if `naryFun!visitors` can't be called with provided arguments.
2861 +/
2862 alias optionalVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.nullable, false);
2863 
2864 ///
2865 @safe pure @nogc nothrow
2866 version(mir_core_test) unittest
2867 {
2868     static struct S { int a; }
2869 
2870     Variant!(S, double) variant;
2871 
2872     alias optionalVisitInst = optionalVisit!((ref value) => value + 0);
2873 
2874     // do nothing because of variant isn't initialized
2875     Nullable!double result = optionalVisitInst(variant);
2876     assert(result.isNull);
2877 
2878     variant = S(2);
2879     // do nothing because of lambda can't compile
2880     result = optionalVisitInst(variant);
2881     assert(result == null);
2882 
2883     variant = 3.0;
2884     result = optionalVisitInst(variant);
2885     assert (result == 3.0);
2886 }
2887 
2888 /++
2889 Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions.
2890 Returns: optionally nullable type, null value is used if `naryFun!visitors` can't be called with provided arguments.
2891 +/
2892 alias autoVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.auto_, false);
2893 
2894 
2895 /++
2896 Applies a delegate or function to the given arguments depending on the held type,
2897 ensuring that all types are handled by the visiting functions.
2898 
2899 The handler supports multiple dispatch or multimethods: a feature of handler in which
2900 a function or method can be dynamically dispatched based on the run time (dynamic) type or,
2901 in the more general case, some other attribute of more than one of its arguments.
2902 
2903 Fuses algebraic types on return.
2904 
2905 See_also: $(HTTPS en.wikipedia.org/wiki/Multiple_dispatch, Multiple dispatch)
2906 +/
2907 alias match(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.compileTime, true);
2908 
2909 ///
2910 version(mir_core_test)
2911 unittest
2912 {
2913     static struct Asteroid { uint size; }
2914     static struct Spaceship { uint size; }
2915     alias SpaceObject = Variant!(Asteroid, Spaceship);
2916 
2917     alias collideWith = match!(
2918         (Asteroid x, Asteroid y) => "a/a",
2919         (Asteroid x, Spaceship y) => "a/s",
2920         (Spaceship x, Asteroid y) => "s/a",
2921         (Spaceship x, Spaceship y) => "s/s",
2922     );
2923 
2924     import mir.utility: min;
2925 
2926     // Direct access of a member in case of all algebraic types has this member
2927     alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1;
2928 
2929     alias collide = (x, y) => oops(x, y) ? "big-boom" : collideWith(x, y);
2930 
2931     auto ea = Asteroid(1);
2932     auto es = Spaceship(2);
2933     auto oa = SpaceObject(ea);
2934     auto os = SpaceObject(es);
2935 
2936     // Asteroid-Asteroid
2937     assert(collide(ea, ea) == "a/a");
2938     assert(collide(ea, oa) == "a/a");
2939     assert(collide(oa, ea) == "a/a");
2940     assert(collide(oa, oa) == "a/a");
2941 
2942     // Asteroid-Spaceship
2943     assert(collide(ea, es) == "a/s");
2944     assert(collide(ea, os) == "a/s");
2945     assert(collide(oa, es) == "a/s");
2946     assert(collide(oa, os) == "a/s");
2947 
2948     // Spaceship-Asteroid
2949     assert(collide(es, ea) == "s/a");
2950     assert(collide(es, oa) == "s/a");
2951     assert(collide(os, ea) == "s/a");
2952     assert(collide(os, oa) == "s/a");
2953 
2954     // Spaceship-Spaceship
2955     assert(collide(es, es) == "big-boom");
2956     assert(collide(es, os) == "big-boom");
2957     assert(collide(os, es) == "big-boom");
2958     assert(collide(os, os) == "big-boom");
2959 }
2960 
2961 /++
2962 Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions.
2963 Throws: Exception if `naryFun!visitors` can't be called with provided arguments
2964 
2965 Fuses algebraic types on return.
2966 +/
2967 alias tryMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.exception, true);
2968 
2969 ///
2970 version(mir_core_test)
2971 unittest
2972 {
2973     import std.exception: assertThrown;
2974     static struct Asteroid { uint size; }
2975     static struct Spaceship { uint size; }
2976     alias SpaceObject = Variant!(Asteroid, Spaceship);
2977 
2978     alias collideWith = tryMatch!(
2979         (Asteroid x, Asteroid y) => "a/a",
2980         // No visitor for A/S pair
2981         // (Asteroid x, Spaceship y) => "a/s",
2982         (Spaceship x, Asteroid y) => "s/a",
2983         (Spaceship x, Spaceship y) => "s/s",
2984     );
2985 
2986     import mir.utility: min;
2987     // Direct access of a member in case of all algebraic types has this member
2988     alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1;
2989 
2990     alias collide = (x, y) => oops(x, y) ? "big-boom" : collideWith(x, y);
2991 
2992     auto ea = Asteroid(1);
2993     auto es = Spaceship(2);
2994     auto oa = SpaceObject(ea);
2995     auto os = SpaceObject(es);
2996 
2997     // Asteroid-Asteroid
2998     assert(collide(ea, ea) == "a/a");
2999     assert(collide(ea, oa) == "a/a");
3000     assert(collide(oa, ea) == "a/a");
3001     assert(collide(oa, oa) == "a/a");
3002 
3003     // Asteroid-Spaceship
3004     assertThrown!Exception(collide(ea, es));
3005     assertThrown!Exception(collide(ea, os));
3006     assertThrown!Exception(collide(oa, es));
3007     assertThrown!Exception(collide(oa, os));
3008 
3009     // can deduce the type based on other return values
3010     static assert(is(typeof(collide(ea, os)) == string));
3011     static assert(is(typeof(collide(oa, es)) == string));
3012     static assert(is(typeof(collide(oa, os)) == string));
3013 
3014     // Also allows newer compilers to detect combinations which always throw an exception
3015     static if (is(typeof(collideWith(ea, es)) == noreturn))
3016     {
3017         static assert(is(typeof(collide(ea, es)) == string));
3018     }
3019     else
3020     {
3021         // not enough information to deduce the type from (ea, es) pair
3022         static assert(is(typeof(collide(ea, es)) == void));
3023     }
3024 
3025     // Spaceship-Asteroid
3026     assert(collide(es, ea) == "s/a");
3027     assert(collide(es, oa) == "s/a");
3028     assert(collide(os, ea) == "s/a");
3029     assert(collide(os, oa) == "s/a");
3030 
3031     // Spaceship-Spaceship
3032     assert(collide(es, es) == "big-boom");
3033     assert(collide(es, os) == "big-boom");
3034     assert(collide(os, es) == "big-boom");
3035     assert(collide(os, os) == "big-boom");
3036 }
3037 
3038 /++
3039 Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions.
3040 Returns: nullable variant, null value is used if `naryFun!visitors` can't be called with provided arguments.
3041 
3042 Fuses algebraic types on return.
3043 +/
3044 alias optionalMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.nullable, true);
3045 
3046 ///
3047 version(mir_core_test)
3048 unittest
3049 {
3050     static struct Asteroid { uint size; }
3051     static struct Spaceship { uint size; }
3052     alias SpaceObject = Variant!(Asteroid, Spaceship);
3053 
3054     alias collideWith = optionalMatch!(
3055         (Asteroid x, Asteroid y) => "a/a",
3056         // No visitor for A/S pair
3057         // (Asteroid x, Spaceship y) => "a/s",
3058         (Spaceship x, Asteroid y) => "s/a",
3059         (Spaceship x, Spaceship y) => "s/s",
3060     );
3061 
3062     import mir.utility: min;
3063     // Direct access of a member in case of all algebraic types has this member
3064     alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1;
3065 
3066     alias collide = (x, y) => oops(x, y) ? "big-boom".nullable : collideWith(x, y);
3067 
3068     auto ea = Asteroid(1);
3069     auto es = Spaceship(2);
3070     auto oa = SpaceObject(ea);
3071     auto os = SpaceObject(es);
3072 
3073     // Asteroid-Asteroid
3074     assert(collide(ea, ea) == "a/a");
3075     assert(collide(ea, oa) == "a/a");
3076     assert(collide(oa, ea) == "a/a");
3077     assert(collide(oa, oa) == "a/a");
3078 
3079     // Asteroid-Spaceship
3080     // assert(collide(ea, es).isNull);  // Compiler error: incompatible types
3081     assert(collideWith(ea, es).isNull); // OK
3082     assert(collide(ea, os).isNull);
3083     assert(collide(oa, es).isNull);
3084     assert(collide(oa, os).isNull);
3085 
3086 
3087     // Spaceship-Asteroid
3088     assert(collide(es, ea) == "s/a");
3089     assert(collide(es, oa) == "s/a");
3090     assert(collide(os, ea) == "s/a");
3091     assert(collide(os, oa) == "s/a");
3092 
3093     // Spaceship-Spaceship
3094     assert(collide(es, es) == "big-boom");
3095     assert(collide(es, os) == "big-boom");
3096     assert(collide(os, es) == "big-boom");
3097     assert(collide(os, os) == "big-boom");
3098 
3099     // check types
3100 
3101     static assert(!__traits(compiles, collide(Asteroid.init, Spaceship.init)));
3102     static assert(is(typeof(collideWith(Asteroid.init, Spaceship.init)) == Nullable!()));
3103 
3104     static assert(is(typeof(collide(Asteroid.init, Asteroid.init)) == Nullable!string));
3105     static assert(is(typeof(collide(Asteroid.init, SpaceObject.init)) == Nullable!string));
3106     static assert(is(typeof(collide(SpaceObject.init, Asteroid.init)) == Nullable!string));
3107     static assert(is(typeof(collide(SpaceObject.init, SpaceObject.init)) == Nullable!string));
3108     static assert(is(typeof(collide(SpaceObject.init, Spaceship.init)) == Nullable!string));
3109     static assert(is(typeof(collide(Spaceship.init, Asteroid.init)) == Nullable!string));
3110     static assert(is(typeof(collide(Spaceship.init, SpaceObject.init)) == Nullable!string));
3111     static assert(is(typeof(collide(Spaceship.init, Spaceship.init)) == Nullable!string));
3112 }
3113 
3114 /++
3115 Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions.
3116 Returns: optionally nullable type, null value is used if `naryFun!visitors` can't be called with provided arguments.
3117 
3118 Fuses algebraic types on return.
3119 +/
3120 alias autoMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.auto_, true);
3121 
3122 ///
3123 version(mir_core_test)
3124 unittest
3125 {
3126     static struct Asteroid { uint size; }
3127     static struct Spaceship { uint size; }
3128     alias SpaceObject = Variant!(Asteroid, Spaceship);
3129 
3130     alias collideWith = autoMatch!(
3131         (Asteroid x, Asteroid y) => "a/a",
3132         // No visitor for A/S pair
3133         // (Asteroid x, Spaceship y) => "a/s",
3134         (Spaceship x, Asteroid y) => "s/a",
3135         (Spaceship x, Spaceship y) => "s/s",
3136     );
3137 
3138     import mir.utility: min;
3139     // Direct access of a member in case of all algebraic types has this member
3140     alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1;
3141 
3142     import mir.conv: to;
3143     alias collide = (x, y) => oops(x, y) ? "big-boom".to!(typeof(collideWith(x, y))) : collideWith(x, y);
3144 
3145     auto ea = Asteroid(1);
3146     auto es = Spaceship(2);
3147     auto oa = SpaceObject(ea);
3148     auto os = SpaceObject(es);
3149 
3150     // Asteroid-Asteroid
3151     assert(collide(ea, ea) == "a/a");
3152     assert(collide(ea, oa) == "a/a");
3153     assert(collide(oa, ea) == "a/a");
3154     assert(collide(oa, oa) == "a/a");
3155 
3156     // Asteroid-Spaceship
3157     // assert(collide(ea, es).isNull);  // Compiler error: incompatible types
3158     assert(collideWith(ea, es).isNull); // OK
3159     assert(collide(ea, os).isNull);
3160     assert(collide(oa, es).isNull);
3161     assert(collide(oa, os).isNull);
3162 
3163     // Spaceship-Asteroid
3164     assert(collide(es, ea) == "s/a");
3165     assert(collide(es, oa) == "s/a");
3166     assert(collide(os, ea) == "s/a");
3167     assert(collide(os, oa) == "s/a");
3168 
3169     // Spaceship-Spaceship
3170     assert(collide(es, es) == "big-boom");
3171     assert(collide(es, os) == "big-boom");
3172     assert(collide(os, es) == "big-boom");
3173     assert(collide(os, os) == "big-boom");
3174 
3175     // check types
3176 
3177     static assert(!__traits(compiles, collide(Asteroid.init, Spaceship.init)));
3178     static assert(is(typeof(collideWith(Asteroid.init, Spaceship.init)) == Nullable!()));
3179 
3180     static assert(is(typeof(collide(Asteroid.init, Asteroid.init)) == string));
3181     static assert(is(typeof(collide(SpaceObject.init, Asteroid.init)) == string));
3182     static assert(is(typeof(collide(Spaceship.init, Asteroid.init)) == string));
3183     static assert(is(typeof(collide(Spaceship.init, SpaceObject.init)) == string));
3184     static assert(is(typeof(collide(Spaceship.init, Spaceship.init)) == string));
3185 
3186     static assert(is(typeof(collide(Asteroid.init, SpaceObject.init)) == Nullable!string));
3187     static assert(is(typeof(collide(SpaceObject.init, SpaceObject.init)) == Nullable!string));
3188     static assert(is(typeof(collide(SpaceObject.init, Spaceship.init)) == Nullable!string));
3189 }
3190 
3191 /++
3192 Applies a member handler to the given Variant depending on the held type,
3193 ensuring that all types are handled by the visiting handler.
3194 +/
3195 alias getMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.compileTime, false);
3196 
3197 ///
3198 @safe pure @nogc nothrow
3199 version(mir_core_test) unittest
3200 {
3201     static struct S { auto bar(int a) { return a; } enum boolean = true; }
3202     static struct C2 { alias bar = (double a) => a * 2; enum boolean = false; }
3203 
3204     alias V = Variant!(S, C2);
3205 
3206     V x = S();
3207     V y = C2();
3208 
3209     static assert(is(typeof(x.getMember!"bar"(2)) == Variant!(int, double)));
3210     assert(x.getMember!"bar"(2) == 2);
3211     assert(y.getMember!"bar"(2) != 4);
3212     assert(y.getMember!"bar"(2) == 4.0);
3213 
3214     // direct implementation
3215     assert(x.bar(2) == 2);
3216     assert(y.bar(2) != 4);
3217     assert(y.bar(2) == 4.0);
3218     assert(x.boolean);
3219     assert(!y.boolean);
3220 }
3221 
3222 /++
3223 Applies a member handler to the given Variant depending on the held type,
3224 ensuring that all types are handled by the visiting handler.
3225 
3226 Fuses algebraic types on return.
3227 +/
3228 alias matchMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.compileTime, true);
3229 
3230 ///
3231 @safe pure @nogc nothrow
3232 version(mir_core_test) unittest
3233 {
3234     static struct S
3235     {
3236         Nullable!int m;
3237     }
3238 
3239     static struct C1
3240     {
3241         Variant!(float, double) m;
3242     }
3243 
3244     alias V = Variant!(S, C1);
3245 
3246     V x = S(2.nullable);
3247     V y = C1(Variant!(float, double)(4.0));
3248 
3249     // getMember returns an algebraic of algebraics
3250     static assert(is(typeof(x.getMember!"m") == Variant!(Variant!(float, double), Nullable!int)));
3251     // matchMember returns a fused algebraic
3252     static assert(is(typeof(x.matchMember!"m") == Nullable!(int, float, double)));
3253     assert(x.matchMember!"m" == 2);
3254     assert(y.matchMember!"m" != 4);
3255     assert(y.matchMember!"m" == 4.0);
3256 }
3257 
3258 /++
3259 Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
3260 Throws: Exception if member can't be accessed with provided arguments
3261 +/
3262 alias tryGetMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.exception, false);
3263 
3264 ///
3265 @safe pure @nogc
3266 version(mir_core_test) unittest
3267 {
3268     static struct S { int bar(int a) { return a; }}
3269     static struct C3 { alias Bar = (double a) => a * 2; }
3270 
3271     alias V = Variant!(S, C3);
3272 
3273     V x = S();
3274     V y = C3();
3275 
3276     static assert(is(typeof(x.tryGetMember!"bar"(2)) == int));
3277     static assert(is(typeof(y.tryGetMember!"Bar"(2)) == double));
3278     assert(x.tryGetMember!"bar"(2) == 2);
3279     assert(y.tryGetMember!"Bar"(2) == 4.0);
3280 }
3281 
3282 ///
3283 @safe pure @nogc nothrow
3284 version(mir_core_test) unittest
3285 {
3286     alias Number = Variant!(int, double);
3287 
3288     Number x = Number(23);
3289     Number y = Number(1.0);
3290 
3291     assert(x.visit!((int v) => true, (float v) => false));
3292     assert(y.visit!((int v) => false, (float v) => true));
3293 }
3294 
3295 /++
3296 Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
3297 Throws: Exception if member can't be accessed with provided arguments
3298 
3299 Fuses algebraic types on return.
3300 +/
3301 alias tryMatchMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.exception, true);
3302 
3303 /++
3304 Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
3305 Returns: nullable variant, null value is used if the member can't be called with provided arguments.
3306 +/
3307 alias optionalGetMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.nullable, false);
3308 
3309 /++
3310 Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
3311 Returns: nullable variant, null value is used if the member can't be called with provided arguments.
3312 
3313 Fuses algebraic types on return.
3314 +/
3315 alias optionalMatchMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.nullable, true);
3316 
3317 /++
3318 Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
3319 Returns: optionally nullable type, null value is used if the member can't be called with provided arguments.
3320 +/
3321 alias autoGetMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.auto_, false);
3322 
3323 /++
3324 Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
3325 Returns: optionally nullable type, null value is used if the member can't be called with provided arguments.
3326 
3327 Fuses algebraic types on return.
3328 +/
3329 alias autoMatchMember(string member, TArgs...) = visitImpl!(getMemberHandler!(member, TArgs), Exhaustive.auto_, true);
3330 
3331 private template getMemberHandler(string member, TArgs...)
3332 {
3333     ///
3334     auto ref getMemberHandler(V, Args...)(ref V value, auto ref Args args)
3335     {
3336         static if (Args.length == 0)
3337         {
3338             static if (TArgs.length)
3339             {
3340                 return mixin(`value.` ~ member ~ `!TArgs`);
3341             }
3342             else
3343             {
3344                 return __traits(getMember, value, member);
3345             }
3346         }
3347         else
3348         {
3349             import core.lifetime: forward;
3350             import mir.reflection: hasField;
3351             static if (TArgs.length)
3352             {
3353                 static if (hasField!(V, member) && Args.length == 1)
3354                     return mixin(`value.` ~ member ~ `!TArgs`) = forward!args;
3355                 else
3356                     return mixin(`value.` ~ member ~ `!TArgs(forward!args)`);
3357             }
3358             else
3359             {
3360                 static if (hasField!(V, member) && Args.length == 1)
3361                     return __traits(getMember, value, member) = forward!args;
3362                 else
3363                     return __traits(getMember, value, member)(forward!args);
3364             }
3365         }
3366     }
3367 }
3368 
3369 private template VariantReturnTypes(T...)
3370 {
3371     import std.meta: staticMap;
3372 
3373     alias VariantReturnTypes = NoDuplicates!(staticMap!(TryRemoveConst, T));
3374 }
3375 
3376 private enum Exhaustive
3377 {
3378     compileTime,
3379     exception,
3380     nullable,
3381     auto_,
3382 }
3383 
3384 private template nextVisitor(T, alias visitor, alias arg)
3385 {
3386     static if (is(T == void))
3387     {
3388         alias nextVisitor = visitor;
3389     }
3390     else
3391     auto ref nextVisitor(NextArgs...)(auto ref NextArgs nextArgs)
3392     {
3393         import core.lifetime: forward;
3394 
3395         static if (__traits(isRef,  arg))
3396             return visitor(arg.trustedGet!T, forward!nextArgs);
3397         else
3398         static if (is(typeof(move(arg.trustedGet!T))))
3399             return visitor(move(arg.trustedGet!T), forward!nextArgs);
3400         else
3401             return visitor((() => arg.trustedGet!T)(), forward!nextArgs);
3402     }
3403 }
3404 
3405 private template nextVisitor(alias visitor, alias arg)
3406 {
3407     auto ref nextVisitor(NextArgs...)(auto ref NextArgs nextArgs)
3408     {
3409         import core.lifetime: forward;
3410         return visitor(forward!arg, forward!nextArgs);
3411     }
3412 }
3413 
3414 private template visitThis(alias visitor, Exhaustive nextExhaustive)
3415 {
3416     template visitThis(T)
3417     {
3418         auto ref visitThis(Args...)(auto ref Args args)
3419         {
3420             import core.lifetime: forward;
3421             return .visitImpl!(nextVisitor!(T, visitor, forward!(args[0])), nextExhaustive, true)(forward!(args[1 .. $]));
3422         }
3423     }
3424 }
3425 
3426 private template visitLast(alias visitor)
3427 {
3428     template visitLast(T)
3429     {
3430         static if (is(T == void))
3431         {
3432             auto ref visitLast(Args...)(auto ref Args args)
3433             {
3434                 import core.lifetime: forward;
3435                 return visitor(forward!(args[1 .. $]));
3436             }
3437         }
3438         else
3439         {
3440             auto ref visitLast(Args...)(auto ref Args args)
3441             {
3442                 import core.lifetime: forward, move;
3443                 static if (__traits(isRef,  args[0]))
3444                     return visitor(args[0].trustedGet!T, forward!(args[1 .. $]));
3445                 else
3446                 static if (is(typeof(move(args[0].trustedGet!T))))
3447                     return visitor(move(args[0].trustedGet!T), forward!(args[1 .. $]));
3448                 else
3449                     return visitor((() => args[0].trustedGet!T)(), forward!(args[1 .. $]));
3450             }
3451         }
3452     }
3453 }
3454 
3455 private enum _AcceptAll(Args...) = true;
3456 
3457 template visitImpl(alias visitor, Exhaustive exhaustive, bool fused, alias Filter = _AcceptAll)
3458 {
3459     ///
3460     auto ref visitImpl(Args...)(auto ref Args args)
3461         if (Filter!Args)
3462     {
3463         import std.meta: anySatisfy, staticMap, AliasSeq;
3464         import core.lifetime: forward;
3465 
3466         static if (!anySatisfy!(isLikeVariant, Args))
3467         {
3468             static if (exhaustive == Exhaustive.compileTime)
3469             {
3470                 return visitor(forward!args);
3471             }
3472             else
3473             static if (exhaustive == Exhaustive.exception)
3474             {
3475                 static if (__traits(compiles, visitor(forward!args)))
3476                     return visitor(forward!args);
3477                 else
3478                     return throwMe(variantMemberException);
3479             }
3480             else
3481             static if (exhaustive == Exhaustive.nullable)
3482             {
3483                 static if (__traits(compiles, visitor(forward!args)))
3484                     return Nullable!(typeof(visitor(forward!args)))(visitor(forward!args));
3485                 else
3486                     return Nullable!().init;
3487             }
3488             else
3489             static if (exhaustive == Exhaustive.auto_)
3490             {
3491                 static if (__traits(compiles, visitor(forward!args)))
3492                     return visitor(forward!args);
3493                 else
3494                     return Nullable!().init;
3495             }
3496             else
3497             static assert(0, "not implemented");
3498         }
3499         else
3500         static if (!isLikeVariant!(Args[0]))
3501         {
3502             return .visitImpl!(nextVisitor!(visitor, args[0]), exhaustive, fused)(forward!(args[1 .. $]));
3503         }
3504         else
3505         {
3506             static if (fused && anySatisfy!(isLikeVariant, Args[1 .. $]))
3507             {
3508                 alias fun = visitThis!(visitor, exhaustive);
3509             }
3510             else
3511             {
3512                 static assert (isLikeVariant!(Args[0]), "First argument should be a Mir Algebraic type");
3513                 alias fun = visitLast!visitor;
3514             }
3515 
3516             template VariantReturnTypesImpl(T)
3517             {
3518                 static if (__traits(compiles, fun!T(forward!args)))
3519                 {
3520                     alias R = typeof(fun!T(forward!args));
3521                     static if (fused && isLikeVariant!R)
3522                         alias VariantReturnTypesImpl = staticMap!(TryRemoveConst, R.AllowedTypes);
3523                     else
3524                     static if (is(immutable R == immutable noreturn))
3525                         alias VariantReturnTypesImpl = AliasSeq!();
3526                     else
3527                         alias VariantReturnTypesImpl = AliasSeq!(TryRemoveConst!R);
3528                 }
3529                 else
3530                 static if (exhaustive == Exhaustive.auto_)
3531                     alias VariantReturnTypesImpl = AliasSeq!(typeof(null));
3532                 else
3533                     alias VariantReturnTypesImpl = AliasSeq!();
3534             }
3535 
3536             static if (exhaustive == Exhaustive.nullable)
3537                 alias AllReturnTypes = NoDuplicates!(typeof(null), staticMap!(VariantReturnTypesImpl, Args[0].AllowedTypes));
3538             else
3539                 alias AllReturnTypes = NoDuplicates!(staticMap!(VariantReturnTypesImpl, Args[0].AllowedTypes));
3540 
3541             switch (args[0].identifier__)
3542             {
3543                 static foreach (i, T; Args[0].AllowedTypes)
3544                 {
3545                     case i:
3546                         static if (__traits(compiles, fun!T(forward!args)) || exhaustive == Exhaustive.compileTime && !is(T == typeof(null)))
3547                         {
3548                             static if (AllReturnTypes.length == 1)
3549                             {
3550                                 return fun!T(forward!args);
3551                             }
3552                             else
3553                             static if (is(VariantReturnTypesImpl!T == AliasSeq!void))
3554                             {
3555                                 fun!T(forward!args);
3556                                 return Variant!AllReturnTypes._void;
3557                             }
3558                             else
3559                             static if (is(typeof(fun!T(forward!args)) == Variant!AllReturnTypes))
3560                             {
3561                                 return fun!T(forward!args);
3562                             }
3563                             else
3564                             {
3565                                 return Variant!AllReturnTypes(fun!T(forward!args));
3566                             }
3567                         }
3568                         else
3569                         static if (exhaustive == Exhaustive.compileTime && is(T == typeof(null)))
3570                         {
3571                             assert(0, "Null " ~ Args[0].stringof);
3572                         }
3573                         else
3574                         static if (exhaustive == Exhaustive.nullable || exhaustive == Exhaustive.auto_)
3575                         {
3576                             return Variant!AllReturnTypes(null);
3577                         }
3578                         else
3579                         {
3580                             return throwMe(variantMemberException);
3581                         }
3582                 }
3583                 default: assert(0);
3584             }
3585         }
3586     }
3587 }
3588 
3589 private string enumKindText()(scope const char[][] strs)
3590 {
3591     auto r = "enum Kind {";
3592     foreach (s; strs)
3593     {
3594         r ~= s;
3595         r ~= ", ";
3596     }
3597     r ~= "}";
3598     return r;
3599 }
3600 
3601 @safe pure @nogc
3602 version(mir_core_test) unittest
3603 {
3604     static struct S { int a; }
3605 
3606     Variant!(S, double) variant;
3607     variant = 1.0;
3608     variant.tryVisit!((ref value, b) => value += b)(2);
3609     assert (variant.get!double == 3);
3610 
3611     alias fun = (ref value) {
3612         static if (is(typeof(value) == S))
3613             value.a += 2;
3614         else
3615            value += 2;
3616     };
3617 
3618     variant.tryVisit!fun;
3619     assert (variant.get!double == 5);
3620 
3621     variant = S(4);
3622     variant.tryVisit!fun;
3623     assert (variant.get!S.a == 6);
3624 
3625     alias D = Variant!(Variant!(S, double));
3626 }
3627 
3628 @safe pure @nogc
3629 version(mir_core_test) unittest
3630 {
3631     import std.meta: AliasSeq;
3632 
3633     static struct PODWithLongPointer {
3634         long* x;
3635         this(long l) pure
3636         {
3637             x = new long(l);
3638         }
3639 
3640     @property:
3641         long a() const {
3642             return x ? *x : 0;
3643         }
3644 
3645         void a(long l) {
3646             if (x) {
3647                 *x = l;
3648             } else {
3649                 x = new long(l);
3650             }
3651         }
3652     }
3653     static assert(is(TypeSet!(byte, immutable PODWithLongPointer) == AliasSeq!(byte, immutable PODWithLongPointer)));
3654 }
3655 
3656 private enum isSimpleAggregateType(T) = is(T == class) || is(T == struct) || is(T == union) || is(T == interface);
3657 
3658 unittest
3659 {
3660     static struct Test
3661     {
3662         alias Value = void;
3663     }
3664 
3665     alias B = Nullable!Test;
3666 }
3667 
3668 /++
3669 Wrapper to denote an error value type.
3670 
3671 The wrapper is autostripped by $(LREF none).
3672 
3673 See_also: $(LREF reflectErr).
3674 +/
3675 template Err(T)
3676 {
3677     static if (!isErr!T)
3678     {
3679         ///
3680         struct Err
3681         {
3682             ///
3683             T value;
3684         }
3685     }
3686     else
3687     {
3688         alias Err = T;
3689     }
3690 }
3691 
3692 /// ditto
3693 auto err(T)(T value) {
3694     import core.lifetime: move;
3695     static if (isErr!T)
3696         return move(value);
3697     else
3698         return Err!T(move(value));
3699 }
3700 
3701 ///
3702 unittest
3703 {
3704     @reflectErr static struct E {}
3705 
3706     static assert(is(Err!string == Err!string));
3707     static assert(is(Err!(Err!string) == Err!string));
3708     static assert(is(Err!E == E));
3709     static assert(is(Err!Exception == Exception));
3710 
3711     static assert(is(typeof("str".err) == Err!string));
3712     static assert(is(typeof(E().err) == E));
3713     static assert(is(typeof(new Exception("str").err) == Exception));
3714 }
3715 
3716 /// Strips out $(LREF Err) wrapper from the type.
3717 template stripErr(T)
3718 {
3719     static if (is(immutable T : immutable Err!U, U))
3720         alias stripErr = U;
3721     else
3722         alias stripErr = T;
3723 }
3724 
3725 ///
3726 version(mir_core_test)
3727 unittest
3728 {
3729     static assert(is(stripErr!Exception == Exception));
3730     static assert(is(stripErr!string == string));
3731     static assert(is(stripErr!(Err!string) == string));
3732 }
3733 
3734 /++
3735 
3736 See_also: $(LREF some) and $(LREF none).
3737 
3738 Params:
3739     visitors = visitors to $(LREF match) with.
3740 +/
3741 alias suit(alias filter, visitors...) = visitImpl!(naryFun!visitors, Exhaustive.compileTime, true, filter);
3742 
3743 ///
3744 version(mir_core_test)
3745 @safe pure nothrow @nogc unittest
3746 {
3747     import std.traits: isDynamicArray, Unqual;
3748     import std.meta: templateNot;
3749     alias V = Variant!(long, int, string, long[], int[]);
3750     alias autoGetElementType = match!(
3751         (string s) => "string", // we override the suit handler below for string
3752         suit!(isDynamicArray, a => Unqual!(typeof(a[0])).stringof),
3753         suit!(templateNot!isDynamicArray, a => Unqual!(typeof(a)).stringof),
3754     );
3755     assert(autoGetElementType(V(string.init)) == "string");
3756     assert(autoGetElementType(V((long[]).init)) == "long");
3757     assert(autoGetElementType(V((int[]).init)) == "int");
3758     assert(autoGetElementType(V(long.init)) == "long");
3759     assert(autoGetElementType(V(int.init)) == "int");
3760 }
3761 
3762 ///
3763 version(mir_core_test)
3764 @safe pure nothrow @nogc unittest
3765 {
3766     import std.traits: allSameType;
3767     import std.meta: templateNot;
3768 
3769     static struct Asteroid { uint size; }
3770     static struct Spaceship { uint size; }
3771     alias SpaceObject = Variant!(Asteroid, Spaceship);
3772 
3773     auto errorMsg = "can't unite an asteroid with a spaceship".err;
3774 
3775     alias unite = match!(
3776         suit!(allSameType, (a, b) => typeof(a)(a.size + b.size)),
3777         suit!(templateNot!allSameType, (a, b) => errorMsg),
3778     );
3779 
3780     auto ea = Asteroid(10);
3781     auto es = Spaceship(1);
3782     auto oa = SpaceObject(ea);
3783     auto os = SpaceObject(es);
3784 
3785     static assert(is(typeof(unite(oa, oa)) == Variant!(Err!string, Asteroid, Spaceship)));
3786 
3787     // Asteroid-Asteroid
3788     assert(unite(ea, ea) == Asteroid(20));
3789     assert(unite(ea, oa) == Asteroid(20));
3790     assert(unite(oa, ea) == Asteroid(20));
3791     assert(unite(oa, oa) == Asteroid(20));
3792 
3793     // Asteroid-Spaceship
3794     assert(unite(ea, es) == errorMsg);
3795     assert(unite(ea, os) == errorMsg);
3796     assert(unite(oa, es) == errorMsg);
3797     assert(unite(oa, os) == errorMsg);
3798 
3799     // Spaceship-Asteroid
3800     assert(unite(es, ea) == errorMsg);
3801     assert(unite(es, oa) == errorMsg);
3802     assert(unite(os, ea) == errorMsg);
3803     assert(unite(os, oa) == errorMsg);
3804 
3805     // Spaceship-Spaceship
3806     assert(unite(es, es) == Spaceship(2));
3807     assert(unite(es, os) == Spaceship(2));
3808     assert(unite(os, es) == Spaceship(2));
3809     assert(unite(os, os) == Spaceship(2));
3810 }
3811 
3812 private template unwrapErrImpl(alias arg)
3813 {
3814     static if (is(immutable typeof(arg) == immutable Err!V, V))
3815         auto ref unwrapErrImpl() @property { return arg.value; }
3816     else
3817         alias unwrapErrImpl = arg;
3818 }
3819 
3820 private template unwrapErr(alias fun)
3821 {
3822     auto ref unwrapErr(Args...)(auto ref return Args args)
3823     {
3824         import std.meta: staticMap;
3825         import std.format: format;
3826         enum expr = () {
3827             string ret = `fun(`;
3828             foreach(i, T; Args)
3829             {
3830                 static if (is(immutable T == immutable Err!V, V))
3831                     ret ~= `args[` ~ i.stringof ~ `].value, `;
3832                 else
3833                     ret ~= `args[` ~ i.stringof ~ `], `;
3834             }
3835             ret ~= `)`;
3836             return ret;
3837         }();
3838         return mixin(expr);
3839     }
3840 }
3841 
3842 /++
3843 $(LREF some) is a variant of $(LREF suit) that forces that type of any argument doesn't satisfy $(LREF isErr) template.
3844 
3845 $(LREF none) is a variant of $(LREF suit) that forces that type of all arguments satisfy $(LREF isErr) template. The handler automatically strips the $(LREF Err) wrapper.
3846 
3847 See_also: $(LREF suit), $(LREF Err), $(LREF isErr),  $(LREF isResultVariant), and $(LREF reflectErr).
3848 
3849 Params:
3850     visitors = visitors to $(LREF match) with.
3851 +/
3852 alias some(visitors...) = suit!(allArgumentsIsNotInstanceOfErr, naryFun!visitors);
3853 
3854 /// ditto
3855 alias none(visitors...) = suit!(anyArgumentIsInstanceOfErr, unwrapErr!(naryFun!visitors));
3856 
3857 ///
3858 version(mir_core_test)
3859 unittest
3860 {
3861     import mir.conv: to;
3862 
3863     alias orElse(alias fun) = visit!(some!"a", none!fun);
3864     alias errToString = orElse!(to!string);
3865 
3866     // can any other type including integer enum
3867     @reflectErr
3868     static struct ErrorInfo {
3869         string msg;
3870         auto toString() const { return msg; }
3871     }
3872 
3873     alias V = Variant!(Err!string, ErrorInfo, Exception, long, double);
3874     alias R = typeof(errToString(V.init));
3875 
3876     static assert(is(R == Variant!(string, long, double)), R.stringof);
3877 
3878     {
3879         V v = 1;
3880         assert(v.isOk);
3881         assert(errToString(v) == 1);
3882     }
3883 
3884     {
3885         V v = 1.0;
3886         assert(v.isOk);
3887         assert(errToString(v) == 1.0);
3888     }
3889 
3890     {
3891         V v = ErrorInfo("msg");
3892         assert(!v.isOk);
3893         assert(errToString(v) == "msg");
3894     }
3895 
3896     {
3897         V v = "msg".err;
3898         assert(!v.isOk);
3899         assert(errToString(v) == "msg");
3900     }
3901 
3902     {
3903         V v = new Exception("msg"); enum line = __LINE__;
3904         assert(!v.isOk);
3905         assert(errToString(v) == "object.Exception@" ~ __FILE__ ~ "(" ~ line.stringof ~ "): msg");
3906     }
3907 }
3908 
3909 /++
3910 Attribute that denotes an error type. Can be used with $(LREF some) and $(LREF none).
3911 
3912 See_also: $(LREF Err).
3913 +/
3914 enum reflectErr;
3915 
3916 /++
3917 Checks if T is a instance of $(LREF Err) or if it is annotated with $(LREF reflectErr).
3918 +/
3919 template isErr(T)
3920 {
3921     import std.traits: isAggregateType, hasUDA;
3922     static if (is(T == enum) || isAggregateType!T)
3923     {
3924         static if (is(immutable T == immutable Err!V, V))
3925         {
3926             enum isErr = true;
3927         }
3928         else
3929         static if (hasUDA!(T, reflectErr))
3930         {
3931             enum isErr = true;
3932         }
3933         else
3934         version (D_Exceptions)
3935         {
3936             enum isErr = is(immutable T : immutable Throwable);
3937         }
3938         else
3939         {
3940             enum isErr = false;
3941         }
3942     }
3943     else
3944     {
3945         enum isErr = false;
3946     }
3947 }
3948 
3949 /++
3950 Checks if T is a Variant with at least one allowed type that satisfy $(LREF isErr) traits.
3951 +/
3952 template isResultVariant(T)
3953 {
3954     static if (is(immutable T == immutable Algebraic!Types, Types...))
3955     {
3956         import std.meta: anySatisfy;
3957         enum isResultVariant = anySatisfy!(isErr, Types);
3958     }
3959     else
3960     {
3961         enum isResultVariant = false;
3962     }
3963 }
3964 
3965 deprecated("Use isResultVariant instead") alias isErrVariant = isResultVariant;
3966 
3967 private template anyArgumentIsInstanceOfErr(Args...)
3968 {
3969     import std.meta: anySatisfy;
3970     enum anyArgumentIsInstanceOfErr = anySatisfy!(isErr, Args);
3971 }
3972 
3973 private template allArgumentsIsNotInstanceOfErr(Args...)
3974 {
3975     import std.meta: anySatisfy;
3976     enum allArgumentsIsNotInstanceOfErr = !anySatisfy!(isErr, Args);
3977 }
3978 
3979 /++
3980 Gets subtype of algebraic without types for which $(LREF isErr) is true.
3981 +/
3982 template SomeVariant(T : Algebraic!Types, Types...)
3983 {
3984     import std.meta: Filter, templateNot;
3985     alias SomeVariant = Algebraic!(Filter!(templateNot!isErr, Types));
3986 }
3987 
3988 ///
3989 @safe pure version(mir_core_test) unittest
3990 {
3991     @reflectErr static struct ErrorS { }
3992     alias V = Variant!(ErrorS, Err!string, long, double, This[]);
3993     static assert(is(SomeVariant!V == Variant!(long, double, This[])));
3994 }
3995 
3996 /++
3997 Gets subtype of algebraic with types for which $(LREF isErr) is true.
3998 +/
3999 template NoneVariant(T : Algebraic!Types, Types...)
4000 {
4001     import std.meta: Filter;
4002     alias NoneVariant = Algebraic!(Filter!(isErr, Types));
4003 }
4004 
4005 ///
4006 @safe pure version(mir_core_test) unittest
4007 {
4008     @reflectErr static struct ErrorS { }
4009     alias V = Variant!(ErrorS, Err!string, long, double, This[]);
4010     static assert(is(NoneVariant!V == Variant!(ErrorS, Err!string)));
4011 }
4012 
4013 private template withNewLine(alias arg)
4014 {
4015     import std.meta: AliasSeq;
4016     alias withNewLine = AliasSeq!("\n", arg);
4017 }
4018 
4019 private noreturn throwMe(T...)(auto ref T args) {
4020     static if (T.length == 1)
4021         enum simpleThrow = is(immutable T[0] : immutable Throwable);
4022     else
4023         enum simpleThrow = false;
4024     static if (simpleThrow)
4025     {
4026         throw args[0].toMutable;
4027     }
4028     else
4029     {
4030         import mir.exception: MirException;
4031         static if (__traits(compiles, { import mir.format: print; }))
4032         {
4033             import std.meta: staticMap;
4034             throw new MirException("assumeOk failure:", staticMap!(withNewLine, args));
4035         }
4036         else
4037         {
4038             import mir.conv: to;
4039             auto msg = "assumeOk failure:";
4040             foreach(ref arg; args)
4041             {
4042                 msg ~= "\n";
4043                 msg ~= arg.to!string;
4044             }
4045             throw new MirException(msg);
4046         }
4047     }
4048 }
4049 
4050 version(D_Exceptions)
4051 /++
4052 Validates that the result doesn't contain an error value.
4053 
4054 Params:
4055     visitor = (compiletime) visitor function. Default value is `naryFun!("", "a")`.
4056     handler = (compiletime) visitor handler to use. Default value is $(LREF match).
4057 Throws:
4058     Throws an exception if at least one parameter passed to
4059     `visitor` satisfies $(LREF isErr) traits.
4060     If there is only one paramter (common case) and its value is `Throwable`, throws it.
4061     Otherwise, _all_ paramters will be printed to the exception message using `mir.format.print`.
4062 +/
4063 alias assumeOk(alias visitor = naryFun!("", "a"), alias handler = .match) = handler!(some!visitor, none!throwMe);
4064 
4065 ///
4066 version(mir_core_test) version(D_Exceptions)
4067 unittest
4068 {
4069     import std.exception: collectExceptionMsg;
4070     import mir.exception: MirException;
4071 
4072     alias SingleTypeValue = typeof(assumeOk(Variant!(Exception, long).init));
4073     static assert(is(SingleTypeValue == long), SingleTypeValue.stringof);
4074 
4075 
4076     // can any other type including integer enum
4077     @reflectErr
4078     static struct ErrorInfo {
4079         string msg;
4080         auto toString() const { return msg; }
4081     }
4082 
4083     alias V = Variant!(Err!string, ErrorInfo, Exception, long, double);
4084     alias R = typeof(assumeOk(V.init));
4085 
4086     static assert(is(R == Variant!(long, double)), R.stringof);
4087 
4088     {
4089         V v = 1;
4090         assert(v.isOk);
4091         assert(v.assumeOk == 1);
4092     }
4093 
4094     {
4095         V v = 1.0;
4096         assert(v.isOk);
4097         assert(v.assumeOk == 1.0);
4098     }
4099 
4100     {
4101         V v = ErrorInfo("msg");
4102         assert(!v.isOk);
4103         assert(v.assumeOk.collectExceptionMsg == "assumeOk failure:\nmsg");
4104     }
4105 
4106     {
4107         V v = "msg".err;
4108         assert(!v.isOk);
4109         assert(v.assumeOk.collectExceptionMsg == "assumeOk failure:\nmsg");
4110     }
4111 
4112     {
4113         V v = new Exception("msg");
4114         assert(!v.isOk);
4115         assert(v.assumeOk.collectExceptionMsg == "msg");
4116     }
4117 }
4118 
4119 version(mir_core_test)
4120 unittest
4121 {
4122     static struct RequestToken
4123     {
4124         Variant!(long, string) value;
4125         alias value this;
4126 
4127         this(T)(T v)
4128         {
4129             value = typeof(value)(v);
4130         }
4131     }
4132 
4133     Variant!(int, RequestToken) v = RequestToken.init;
4134 
4135     auto r = v.match!(
4136         (int v) {
4137             return assert(false);
4138         },
4139         ret => ret
4140     );
4141 
4142     static assert(is(typeof(r) == Variant!(long, string)));
4143 }
4144 
4145 package auto trustedAllAttr(T)(scope T t) @trusted
4146 {
4147     import std.traits;
4148     enum attrs = (functionAttributes!T & ~FunctionAttribute.system)
4149         | FunctionAttribute.pure_
4150         | FunctionAttribute.safe
4151         | FunctionAttribute.nogc
4152         | FunctionAttribute.nothrow_;
4153     return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
4154 }
4155 
4156 private static immutable algebraicMembers = [
4157     "_is",
4158     "_void",
4159     "AllowedTypes",
4160     "MetaFieldsTypes",
4161     "get",
4162     "isNull",
4163     "kind",
4164     "Kind",
4165     "nullify",
4166     "opAssign",
4167     "opCast",
4168     "opCmp",
4169     "opEquals",
4170     "opPostMove",
4171     "toHash",
4172     "toString",
4173     "trustedGet",
4174     "deserializeFromAsdf",
4175     "deserializeFromIon",
4176 ];
4177 
4178 private template UnqualRec(T)
4179 {
4180     import std.traits: Unqual, isDynamicArray, ForeachType;
4181     static if (isDynamicArray!T)
4182         alias UnqualRec = UnqualRec!(ForeachType!T)[];
4183     else
4184         alias UnqualRec = Unqual!T;
4185 }