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 Algebraic first argument) $(TH Fuses Algebraic types on return))
27 $(LEADINGROWN 8, Classic handlers)
28 $(T8 visit, Yes, N/A, No, No, 1+, Yes, No)
29 $(T8 optionalVisit, No, No, Yes, No, 1+, Yes, No)
30 $(T8 autoVisit, No, No, auto, No, 1+, Yes, No)
31 $(T8 tryVisit, No, Yes, No, No, 1+, Yes, No)
32 $(LEADINGROWN 8, Multiple dispatch and algebraic fusion on return)
33 $(T8 match, Yes, N/A, No, Yes, 0+, auto, Yes)
34 $(T8 optionalMatch, No, No, Yes, Yes, 0+, auto, Yes)
35 $(T8 autoMatch, No, No, auto, Yes, 0+, auto, Yes)
36 $(T8 tryMatch, No, Yes, No, Yes, 0+, auto, Yes)
37 $(LEADINGROWN 8, Member access)
38 $(T8 getMember, Yes, N/A, No, No, 1+, Yes, No)
39 $(T8 optionalGetMember, No, No, Yes, No, 1+, Yes, No)
40 $(T8 autoGetMember, No, No, auto, No, 1+, Yes, No)
41 $(T8 tryGetMember, No, Yes, No, No, 1+, Yes, No)
42 $(LEADINGROWN 8, Member access with algebraic fusion on return)
43 $(T8 matchMember, Yes, N/A, No, No, 1+, Yes, Yes)
44 $(T8 optionalMatchMember, No, No, Yes, No, 1+, Yes, Yes)
45 $(T8 autoMatchMember, No, No, auto, No, 1+, Yes, Yes)
46 $(T8 tryMatchMember, No, Yes, No, No, 1+, Yes, Yes)
47 )
48 
49 $(BOOKTABLE $(H3 Special Types),
50 $(TR $(TH Name) $(TH Description))
51 $(T2plain `void`, It is usefull to indicate a possible return type of the visitor. Can't be accesed by reference. )
52 $(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. )
53 $(T2 This, Dummy structure that is used to construct self-referencing algebraic types. Example: `Variant!(int, double, string, This*[2])`)
54 $(T2plain $(LREF SetAlias)`!setId`, Dummy structure that is used to construct cyclic-referencing lists of algebraic types. )
55 $(T2 TaggedType, Dummy type used to associate tags with type. )
56 )
57 
58 $(BOOKTABLE $(H3 $(LREF Algebraic) Traits),
59 $(TR $(TH Name) $(TH Description))
60 $(T2 isVariant, Checks if the type is instance of $(LREF Algebraic).)
61 $(T2 isNullable, Checks if the type is instance of $(LREF Algebraic) with a self $(LREF TypeSet) that contains `typeof(null)`. )
62 $(T2 isTaggedVariant, Checks if the type is instance of tagged $(LREF Algebraic).)
63 $(T2 isTypeSet, Checks if the types are the same as $(LREF TypeSet) of them. )
64 $(T2 ValueTypeOfNullable, Gets type of $(LI $(LREF .Algebraic.get.2)) method. )
65 
66 )
67 
68 
69 $(H3 Type Set)
70 $(UL 
71 $(LI $(LREF TaggedTypeSet) is supported. Example:`TargetTypeSet!(["integer", "floating"], int, double)`).
72 $(LI Type set is unordered. Example:`TypeSet!(int, double)` and `TypeSet!(double, int)` are the same. )
73 $(LI Duplicats are ignored. Example: `TypeSet!(float, int, float)` and `TypeSet!(int, float)` are the same. )
74 $(LI Types are automatically unqualified if this operation can be performed implicitly. Example: `TypeSet!(const int) and `TypeSet!int` are the same. )
75 $(LI Non trivial `TypeSet!(A, B, ..., etc)` is allowed.)
76 $(LI Trivial `TypeSet!T` is allowed.)
77 $(LI Empty `TypeSet!()` is allowed.)
78 )
79 
80 $(H3 Visitors)
81 $(UL 
82 $(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. )
83 $(LI Visitors are allowed to accept additional arguments. The arguments can be passed to the visitor handler. )
84 $(LI Multiple visitors can be passes to the visitor handler. )
85 $(LI Visitors are matched according to the common $(HTTPS dlang.org/spec/function.html#function-overloading, Dlang Function Overloading) rules. )
86 $(LI Visitors are allowed accept algebraic value by reference except the value of `typeof(null)`. )
87 $(LI Visitors are called without algebraic value if its algebraic type is `void`. )
88 $(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. )
89 )
90 
91 $(H3 Implementation Features)
92 $(UL 
93 $(LI BetterC support. Runtime `TypeInfo` is not used.)
94 $(LI Copy-constructors and postblit constructors are supported. )
95 $(LI `toHash`, `opCmp`. `opEquals`, and `toString` support. )
96 $(LI No string or template mixins are used. )
97 $(LI Optimised for fast execution. )
98 )
99 
100 See_also: $(HTTPS en.wikipedia.org/wiki/Algebra_of_sets, Algebra of sets).
101 
102 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
103 Authors: Ilya Yaroshenko
104 
105 Macros:
106 T2plain=$(TR $(TDNW $1) $(TD $+))
107 T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
108 T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4))
109 T8=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4) $(TD $5) $(TD $6) $(TD $7) $(TD $8))
110 
111 +/
112 module mir.algebraic;
113 
114 import mir.internal.meta;
115 import mir.functional: naryFun;
116 
117 private static immutable variantExceptionMsg = "mir.algebraic: the algebraic stores other type then requested.";
118 private static immutable variantNullExceptionMsg = "mir.algebraic: the algebraic is empty and doesn't store any value.";
119 private static immutable variantMemberExceptionMsg = "mir.algebraic: the algebraic stores a type that isn't compatible with the user provided visitor and arguments.";
120 
121 version (D_Exceptions)
122 {
123     private static immutable variantException = new Exception(variantExceptionMsg);
124     private static immutable variantNullException = new Exception(variantNullExceptionMsg);
125     private static immutable variantMemberException = new Exception(variantMemberExceptionMsg);
126 }
127 
128 private static struct _Null()
129 {
130 @safe pure nothrow @nogc const:
131     int opCmp(_Null) { return 0; }
132     this(typeof(null)) inout {}
133     string toString() { return "null"; }
134 }
135 
136 private static struct _Void()
137 {
138  @safe pure nothrow @nogc const:
139     int opCmp(_Void) { return 0; }
140     string toString() { return "void"; }
141 }
142 
143 /++
144 Checks if the type is instance of $(LREF Algebraic).
145 +/
146 enum bool isVariant(T) = is(T == Algebraic!Types, Types...);
147 
148 ///
149 @safe pure version(mir_core_test) unittest
150 {
151     static assert(isVariant!(Variant!(int, string)));
152     static assert(isVariant!(const Variant!(int[], string)));
153     static assert(isVariant!(Nullable!(int, string)));
154     static assert(!isVariant!int);
155 }
156 
157 /++
158 Checks if the type is instance of tagged $(LREF Algebraic).
159 
160 Tagged algebraics can be defined with $(LREF TaggedVariant).
161 +/
162 enum bool isTaggedVariant(T) = isVariant!T && is(T.Kind == enum);
163 
164 ///
165 @safe pure version(mir_core_test) unittest
166 {
167     static assert(!isTaggedVariant!int);
168     static assert(!isTaggedVariant!(Variant!(int, string)));
169     static assert(isTaggedVariant!(TaggedVariant!(["integer", "string"], int, string)));
170 }
171 
172 /++
173 Checks if the type is instance of $(LREF Algebraic) with a self $(LREF TypeSet) that contains `typeof(null)`.
174 +/
175 enum bool isNullable(T) = is(T == Algebraic!(typeof(null), Types), Types...);
176 
177 ///
178 @safe pure version(mir_core_test) unittest
179 {
180     static assert(isNullable!(const Nullable!(int, string)));
181     static assert(isNullable!(Nullable!()));
182 
183     static assert(!isNullable!(Variant!()));
184     static assert(!isNullable!(Variant!string));
185     static assert(!isNullable!int);
186     static assert(!isNullable!string);
187 }
188 
189 /++
190 Gets type of $(LI $(LREF .Algebraic.get.2)) method.
191 +/
192 template ValueTypeOfNullable(T : Algebraic!(typeof(null), Types), Types...)
193 {
194     static if (Types.length == 1)
195         alias ValueTypeOfNullable = Types[0];
196     else
197         alias ValueTypeOfNullable = Algebraic!Types;
198 }
199 
200 ///
201 @safe pure version(mir_core_test) unittest
202 {
203     static assert(is(ValueTypeOfNullable!(const Nullable!(int, string)) == Algebraic!(int, string)));
204     static assert(is(ValueTypeOfNullable!(Nullable!()) == Algebraic!()));
205     static assert(is(typeof(Nullable!().get()) == Algebraic!()));
206 }
207 
208 /++
209 Dummy type for $(LREF Variant) and $(LREF Nullable) self-referencing.
210 +/
211 struct This
212 {
213 @safe pure nothrow @nogc const:
214     int opCmp(typeof(this)) { return 0; }
215     string toString() { return typeof(this).stringof; }
216 }
217 
218 /++
219 Dummy type used to associate tags with a type.
220 +/
221 struct TaggedType(T, string name)
222     if (name.length)
223 {
224     private enum tag = name;
225     private alias Type = T;
226     static if (!is(T == void) && !is(T == typeof(null)))
227         private T payload;
228 @safe pure nothrow @nogc const:
229     int opCmp(ref typeof(this)) { return 0; }
230     string toString() { return typeof(this).stringof; }
231 }
232 
233 /++
234 Checks if `T` is $(LREF TaggedType) instance.
235 +/
236 enum isTaggedType(T) = is(T == TaggedType!(I, name), I, string name);
237 
238 /++
239 Gets $(LREF TaggedType) underlying type.
240 +/
241 alias getTaggedTypeUnderlying(T : TaggedType!(I, name), I, string name) = I;
242 
243 /++
244 Gets $(LREF TaggedType) tag name.
245 +/
246 enum getTaggedTypeName(T : TaggedType!(I, name), I, string name) = name;
247 
248 // example from std.variant
249 /++
250 $(H4 Self-Referential Types)
251 A useful and popular use of algebraic data structures is for defining
252 $(LUCKY self-referential data structures), i.e. structures that embed references to
253 values of their own type within.
254 This is achieved with $(LREF Variant) by using $(LREF This) as a placeholder whenever a
255 reference to the type being defined is needed. The $(LREF Variant) instantiation
256 will perform 
257 $(LINK2 https://en.wikipedia.org/wiki/Name_resolution_(programming_languages)#Alpha_renaming_to_make_name_resolution_trivial,
258 alpha renaming) on its constituent types, replacing $(LREF This)
259 with the self-referenced type. The structure of the type involving $(LREF This) may
260 be arbitrarily complex.
261 +/
262 @safe pure version(mir_core_test) unittest
263 {
264     import mir.functional: Tuple = RefTuple;
265 
266     // A tree is either a leaf or a branch of two others
267     alias Tree(Leaf) = Variant!(Leaf, Tuple!(This*, This*));
268     alias Leafs = Tuple!(Tree!int*, Tree!int*);
269 
270     Tree!int tree = Leafs(new Tree!int(41), new Tree!int(43));
271     Tree!int* right = tree.get!Leafs[1];
272     assert(*right == 43);
273 }
274 
275 ///
276 @safe pure version(mir_core_test) unittest
277 {
278     // An object is a double, a string, or a hash of objects
279     alias Obj = Variant!(double, string, This[string], This[]);
280     alias Map = Obj[string];
281 
282     Obj obj = "hello";
283     assert(obj._is!string);
284     assert(obj.trustedGet!string == "hello");
285     obj = 42.0;
286     assert(obj.get!double == 42);
287     obj = ["customer": Obj("John"), "paid": Obj(23.95)];
288     assert(obj.get!Map["customer"] == "John");
289 }
290 
291 version(mir_core_test) unittest
292 {
293    static struct Foo
294    {
295       ~this() @system
296       {
297       }
298    }
299    alias TFoo = TaggedType!(Foo, "foo");
300    TFoo foo;
301 }
302 
303 
304 /++
305 Type set resolution template used to construct $(LREF Algebraic) .
306 +/
307 template TypeSet(T...)
308 {
309     import std.meta: staticSort, staticMap, allSatisfy, anySatisfy;
310     // sort types by sizeof and them mangleof
311     // but typeof(null) goes first
312     static if (is(staticMap!(TryRemoveConst, T) == T))
313         static if (is(NoDuplicates!T == T))
314             static if (staticIsSorted!(TypeCmp, T))
315             {
316                 static if (anySatisfy!(isTaggedType, T))
317                 {
318                     import std.meta: Filter, templateNot;
319                     static assert(Filter!(templateNot!isTaggedType, T).length == 0,
320                         "Either all or none types must be tagged. Types that doesn't have tags: " ~
321                         Filter!(templateNot!isTaggedType, T).stringof);
322                 }
323                 alias TypeSet = T;
324             }
325             else
326                 alias TypeSet = .TypeSet!(staticSort!(TypeCmp, T));
327         else
328             alias TypeSet = TypeSet!(NoDuplicates!T);
329     else
330         alias TypeSet = TypeSet!(staticMap!(TryRemoveConst, T));
331 }
332 
333 private template TypeCmp(A, B)
334 {
335     enum bool TypeCmp = is(A == B) ? false:
336     is(A == typeof(null)) || is(A == TaggedType!(typeof(null), aname), string aname) ? true:
337     is(B == typeof(null)) || is(B == TaggedType!(typeof(null), bname), string bname) ? false:
338     is(A == void) || is(A == TaggedType!(void, aname), string aname) ? true:
339     is(B == void) || is(A == TaggedType!(void, bname), string bname) ? false:
340     A.sizeof < B.sizeof ? true:
341     A.sizeof > B.sizeof ? false:
342     A.mangleof < B.mangleof;
343 }
344 
345 ///
346 version(mir_core_test) unittest
347 {
348     struct S {}
349     alias C = S;
350     alias Int = int;
351     static assert(is(TypeSet!(S, int) == TypeSet!(Int, C)));
352     static assert(is(TypeSet!(S, int, int) == TypeSet!(Int, C)));
353     static assert(!is(TypeSet!(uint, S) == TypeSet!(int, S)));
354 }
355 
356 private template applyTags(string[] tagNames, T...)
357     if (tagNames.length == T.length)
358 {
359     import std.meta: AliasSeq;
360     static if (tagNames.length == 0)
361         alias applyTags = AliasSeq!();
362     else
363         alias applyTags =  AliasSeq!(TaggedType!(T[0], tagNames[0]), .applyTags!(tagNames[1 .. $], T[1 .. $]));
364 }
365 
366 /++
367 Type set for tagged $(LREF Variants) self-referencing.
368 +/
369 alias TaggedTypeSet(string[] tagNames, T...) = TypeSet!(applyTags!(tagNames, T));
370 
371 /++
372 Checks if the type list is $(LREF TypeSet).
373 +/
374 enum bool isTypeSet(T...) = is(T == TypeSet!T);
375 
376 ///
377 @safe pure version(mir_core_test) unittest
378 {
379     static assert(isTypeSet!(TypeSet!()));
380     static assert(isTypeSet!(TypeSet!void));
381     static assert(isTypeSet!(TypeSet!(void, int, typeof(null))));
382 }
383 
384 /++
385 Variant Type (aka Algebraic Type).
386 
387 The impllementation is defined as
388 ----
389 alias Variant(T...) = Algebraic!(TypeSet!T);
390 ----
391 
392 Compatible with BetterC mode.
393 +/
394 alias Variant(T...) = Algebraic!(TypeSet!T);
395 
396 ///
397 @safe pure @nogc 
398 version(mir_core_test) unittest
399 {
400     Variant!(int, double, string) v = 5;
401     assert(v.get!int == 5);
402     v = 3.14;
403     assert(v == 3.14);
404     // auto x = v.get!long; // won't compile, type long not allowed
405     // v = '1'; // won't compile, type char not allowed
406 }
407 
408 /// Single argument Variant
409 // and Type with copy constructor
410 @safe pure nothrow @nogc 
411 version(mir_core_test) unittest 
412 {
413     static struct S
414     {
415         int n;
416         this(ref return scope inout S rhs) inout
417         {
418             this.n = rhs.n + 1;
419         }
420     }
421 
422     Variant!S a = S();
423     auto b = a;
424 
425     import mir.conv;
426     assert(a.get!S.n == 0);
427     assert(b.n == 1); //direct access of a member in case of all algebraic types has this member
428 }
429 
430 /// Empty type set
431 @safe pure nothrow @nogc version(mir_core_test) unittest 
432 {
433     Variant!() a;
434     auto b = a;
435     assert(a.toHash == 0);
436     assert(a == b);
437     assert(a <= b && b >= a);
438     static assert(typeof(a).sizeof == 1);
439 }
440 
441 /// Small types
442 @safe pure nothrow @nogc version(mir_core_test) unittest 
443 {
444     struct S { ubyte d; }
445     static assert(Nullable!(byte, char, S).sizeof == 2);
446 }
447 
448 @safe pure nothrow @nogc version(mir_core_test) unittest 
449 {
450     struct S { ubyte[3] d; }
451     static assert(Nullable!(ushort, wchar, S).sizeof == 6);
452 }
453 
454 // /// opPostMove support
455 // @safe pure @nogc nothrow
456 // version(mir_core_test) unittest
457 // {
458 //     import std.algorithm.mutation: move;
459 
460 //     static struct S
461 //     {
462 //         uint s;
463 
464 //         void opPostMove(const ref S old) nothrow
465 //         {
466 //             this.s = old.s + 1;
467 //         }
468 //     }
469 
470 //     Variant!S a;
471 
472 //     auto b = a.move;
473 //     assert(b.s == 1);
474 // }
475 
476 /++
477 Tagged Variant Type (aka Tagged Algebraic Type).
478 
479 Compatible with BetterC mode.
480 
481 Template has two declarations:
482 ----
483 alias TaggedVariant(string[] tags, T...) = Variant!(applyTags!(tags, T));
484 // and
485 template TaggedVariant(T)
486     if (is(T == union))
487 {
488     ...
489 }
490 ----
491 
492 See_also: $(LREF Variant), $(LREF isTaggedVariant).
493 +/
494 alias TaggedVariant(string[] tags, T...) = Variant!(applyTags!(tags, T));
495 
496 /// ditto
497 template TaggedVariant(T)
498     if (is(T == union))
499 {
500     import std.meta: staticMap;
501     enum names = __traits(allMembers, T);
502     alias TypeOf(string member) = typeof(__traits(getMember, T, member));
503     alias Types = staticMap!(TypeOf, names);
504     alias TaggedVariant = .TaggedVariant!([names], Types);
505 }
506 
507 /// Json Value
508 @safe pure 
509 version(mir_core_test) unittest
510 {
511     static union JsonUnion
512     {
513         long integer;
514         double floating;
515         bool boolean;
516         typeof(null) null_;
517         string string_;
518         This[] array;
519         This[string] object;
520     }
521 
522     alias JsonValue = TaggedVariant!JsonUnion;
523 
524     // typeof(null) has priority
525     static assert(JsonValue.Kind.init == JsonValue.Kind.null_);
526     static assert(JsonValue.Kind.null_ == 0);
527     
528     // Kind and AllowedTypes has the same order
529     static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.array] == JsonValue[]));
530     static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.boolean] == bool));
531     static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.floating] == double));
532     static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.integer] == long));
533     static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.null_] == typeof(null)));
534     static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.object] == JsonValue[string]));
535 
536     JsonValue v;
537     assert(v.kind == JsonValue.Kind.null_);
538 
539     v = 1;
540     assert(v.kind == JsonValue.Kind.integer);
541     assert(v == 1);
542     v = JsonValue(1);
543     assert(v == 1);
544     v = v.get!(long, double);
545 
546     v = "Tagged!";
547     assert(v.get       !string                  == "Tagged!");
548     assert(v.trustedGet!string                  == "Tagged!");
549 
550     assert(v.kind == JsonValue.Kind.string_);
551 
552     assert(v.get       !(JsonValue.Kind.string_) == "Tagged!"); // Kind-based get
553     assert(v.trustedGet!(JsonValue.Kind.string_) == "Tagged!"); // Kind-based trustedGet
554 
555     v = [JsonValue("str"), JsonValue(4.3)];
556 
557     assert(v.kind == JsonValue.Kind.array);
558     assert(v.trustedGet!(JsonValue[])[1].kind == JsonValue.Kind.floating);
559 
560     v = null;
561     assert(v.kind == JsonValue.Kind.null_);
562 }
563 
564 /// Wrapped algebraic with propogated primitives
565 @safe pure 
566 version(mir_core_test) unittest
567 {
568     static struct Response
569     {
570         alias Union = TaggedVariant!(
571             ["double_", "string", "array", "table"],
572             double,
573             string,
574             Response[],
575             Response[string],
576         );
577 
578         Union data;
579         alias Tag = Union.Kind;
580         // propogates opEquals, opAssign, and other primitives
581         alias data this;
582 
583         static foreach (T; Union.AllowedTypes)
584             this(T v) @safe pure nothrow @nogc { data = v; }
585     }
586 
587     Response v = 3.0;
588     assert(v.kind == Response.Tag.double_);
589     v = "str";
590     assert(v == "str");
591 }
592 
593 /++
594 Nullable $(LREF Variant) Type (aka Algebraic Type).
595 
596 The impllementation is defined as
597 ----
598 alias Nullable(T...) = Variant!(typeof(null), T);
599 ----
600 
601 In additional to common algebraic API the following members can be accesssed:
602 $(UL 
603 $(LI $(LREF .Algebraic.isNull))
604 $(LI $(LREF .Algebraic.nullify))
605 $(LI $(LREF .Algebraic.get.2))
606 )
607 
608 Compatible with BetterC mode.
609 +/
610 alias Nullable(T...) = Variant!(typeof(null), T);
611 
612 /// ditto
613 Nullable!T nullable(T)(T t)
614 {
615     import core.lifetime: forward;
616     return Nullable!T(forward!t);
617 }
618 
619 /++
620 Single type `Nullable`
621 +/
622 @safe pure @nogc
623 version(mir_core_test) unittest
624 {
625     static assert(is(Nullable!int == Variant!(typeof(null), int)));
626     
627     Nullable!int a = 5;
628     assert(a.get!int == 5);
629 
630     a.nullify;
631     assert(a.isNull);
632 
633     a = 4;
634     assert(!a.isNull);
635     assert(a.get == 4);
636     assert(a == 4);
637     a = 4;
638 
639     a = null;
640     assert(a == null);
641 }
642 
643 /// Empty nullable type set support
644 @safe pure nothrow @nogc version(mir_core_test) unittest 
645 {
646     Nullable!() a;
647     auto b = a;
648     assert(a.toHash == 0);
649     assert(a == b);
650     assert(a <= b && b >= a);
651     static assert(typeof(a).sizeof == 1);
652 }
653 
654 /++
655 Algebraic implementation.
656 For more portable code, it is higly recommeded to don't use this template directly.
657 Instead, please use of $(LREF Variant) and $(LREF Nullable), which sort types.
658 +/
659 struct Algebraic(_Types...)
660 {
661     import core.lifetime: moveEmplace;
662     import mir.conv: emplaceRef;
663     import mir.reflection: isPublic, hasField, isProperty;
664     import std.meta: Filter, AliasSeq, ApplyRight, anySatisfy, allSatisfy, staticMap, templateOr, templateNot;
665     import std.traits:
666         hasElaborateAssign,
667         hasElaborateCopyConstructor,
668         hasElaborateDestructor,
669         hasMember,
670         isEqualityComparable,
671         isOrderingComparable,
672         Largest,
673         Unqual
674         ;
675 
676     private enum bool _variant_test_ = is(_Types == AliasSeq!(typeof(null), double));
677 
678     static if (anySatisfy!(isTaggedType, _Types))
679     {
680         private alias _UntaggedThisTypeSetList = staticMap!(getTaggedTypeUnderlying, _Types);
681     }
682     else
683     {
684         private alias _UntaggedThisTypeSetList = _Types;
685     }
686 
687     /++
688     Allowed types list
689     See_also: $(LREF TypeSet)
690     +/
691     alias AllowedTypes = AliasSeq!(ReplaceTypeUnless!(isVariant, This, Algebraic!_Types, _UntaggedThisTypeSetList));
692 
693     version(mir_core_test)
694     static if (_variant_test_)
695     ///
696     unittest
697     {
698         import std.meta: AliasSeq;
699 
700         alias V = Nullable!
701         (
702             This*,
703             string,
704             double,
705             bool,
706         );
707 
708         static assert(is(V.AllowedTypes == TypeSet!(
709             typeof(null),
710             bool,
711             string,
712             double,
713             V*)));
714     }
715 
716     private alias _Payload = Replace!(void, _Void!(), Replace!(typeof(null), _Null!(), AllowedTypes));
717 
718     private static union _Storage_
719     {
720         _Payload payload;
721 
722         static foreach (int i, P; _Payload)
723             mixin(`alias _member_` ~ i.stringof ~ ` = payload[` ~ i.stringof ~ `];`);
724 
725         static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null))))
726             ubyte[0] bytes;
727         else
728             ubyte[Largest!_Payload.sizeof] bytes;
729     }
730 
731     private _Storage_ _storage_;
732 
733     static if (AllowedTypes.length > 1)
734     {
735         static if ((_Storage_.alignof & 1) && _Payload.length <= ubyte.max)
736             private alias _ID_ = ubyte;
737         else
738         static if ((_Storage_.alignof & 2) && _Payload.length <= ushort.max)
739             private alias _ID_ = ushort;
740         else
741         static if (_Storage_.alignof & 3)
742             private alias _ID_ = uint;
743         else
744             private alias _ID_ = ulong;
745     
746         _ID_ _identifier_;
747     }
748     else
749     {
750         alias _ID_ = uint;
751         enum _ID_ _identifier_ = 0;
752     }
753 
754     version (D_Ddoc)
755     {
756         /++
757         Algebraic Kind.
758 
759         Defined as enum for tagged algebraics and as unsigned for common algebraics.
760 
761         The Kind enum contains the members defined using tag names.
762 
763         If the algebraic type is $(LREF Nullable) then the default Kind enum member has zero value and corresponds to `typeof(null)`.
764 
765         See_also: $(LREF TaggedVariant).
766         +/
767         enum Kind { _not_me_but_tags_name_list_ }
768     }
769 
770     static if (anySatisfy!(isTaggedType, _Types))
771     {
772         version (D_Ddoc){}
773         else
774         {
775             mixin(enumKindText([staticMap!(getTaggedTypeName, _Types)]));
776 
777         }
778     }
779     else
780     {
781         version (D_Ddoc){}
782         else
783         {
784             alias Kind = _ID_;
785         }
786     }
787 
788     /++
789     Returns: $(LREF .Algebraic.Kind).
790 
791     Defined as enum for tagged algebraics and as unsigned for common algebraics.
792     See_also: $(LREF TaggedVariant).
793     +/
794     Kind kind() const @safe pure nothrow @nogc @property
795     {
796         assert(_identifier_ <= Kind.max);
797         return cast(Kind) _identifier_;
798     }
799 
800     static if (anySatisfy!(hasElaborateDestructor, _Payload))
801     ~this() @trusted
802     {
803         S: switch (_identifier_)
804         {
805             static foreach (i, T; AllowedTypes)
806             static if (hasElaborateDestructor!T)
807             {
808                 case i:
809                     (*cast(Unqual!(_Payload[i])*)&_storage_.payload[i]).__xdtor;
810                     break S;
811             }
812             default:
813         }
814         version(mir_secure_memory)
815             _storage_.bytes = 0xCC;
816     }
817 
818     // static if (anySatisfy!(hasOpPostMove, _Payload))
819     // void opPostMove(const ref typeof(this) old)
820     // {
821     //     S: switch (_identifier_)
822     //     {
823     //         static foreach (i, T; AllowedTypes)
824     //         static if (hasOpPostMove!T)
825     //         {
826     //             case i:
827     //                 this._storage_.payload[i].opPostMove(old._storage_.payload[i]);
828     //                 return;
829     //         }
830     //         default: return;
831     //     }
832     // }
833 
834     static if (AllowedTypes.length)
835     {
836         static if (!__traits(compiles, (){ _Payload[0] arg; }))
837         {
838             @disable this();
839         }
840     }
841 
842     /// Construct an algebraic type from its subset.
843     this(RhsTypes...)(Algebraic!RhsTypes rhs)
844         if (allSatisfy!(Contains!AllowedTypes, Algebraic!RhsTypes.AllowedTypes))
845     {
846         import core.lifetime: move;
847         static if (is(RhsTypes == _Types))
848             this = move(rhs);
849         else
850         {
851             switch (rhs._identifier_)
852             {
853                 static foreach (i, T; Algebraic!RhsTypes.AllowedTypes)
854                 {
855                     case i:
856                         static if (__traits(compiles, __ctor(move(rhs.trustedGet!T))))
857                             __ctor(move(rhs.trustedGet!T));
858                         else
859                             __ctor(rhs.trustedGet!T);
860                         return;
861                 }
862                 default:
863                     assert(0, variantMemberExceptionMsg);
864             }
865         }
866     }
867 
868     version(mir_core_test)
869     static if (_variant_test_)
870     ///
871     unittest
872     {
873         alias Float = Variant!(float, double);
874         alias Int = Variant!(long, int);
875         alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes);
876 
877         Float fp = 3.0;
878         Number number = fp; // constructor call
879         assert(number == 3.0);
880 
881         Int integer = 12L;
882         number = Number(integer);
883         assert(number == 12L);
884     }
885 
886     static if (!allSatisfy!(isCopyable, AllowedTypes))
887         @disable this(this);
888     else
889     static if (anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
890     {
891         // private enum _allCanImplicitlyRemoveConst = allSatisfy!(canImplicitlyRemoveConst, AllowedTypes);
892         // private enum _allCanRemoveConst = allSatisfy!(canRemoveConst, AllowedTypes);
893         // private enum _allHaveImplicitSemiMutableConstruction = _allCanImplicitlyRemoveConst && _allHaveMutableConstruction;
894 
895         static if (__VERSION__ < 2094)
896         private static union _StorageI(uint i)
897         {
898             _Payload[i] payload;
899             ubyte[_Storage_.bytes.length] bytes;
900         }
901 
902         static if (allSatisfy!(hasInoutConstruction, AllowedTypes))
903         this(return ref scope inout Algebraic rhs) inout
904         {
905             static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_;
906             static foreach (int i, T; AllowedTypes)
907             static if (!is(T == typeof(null)) && !is(T == void))
908             {
909                 if (_identifier_ == i)
910                 {
911                     static if (__VERSION__ < 2094)
912                     {
913                         _storage_.bytes = () inout @trusted {
914                             auto ret =  inout _StorageI!i(rhs.trustedGet!T);
915                             return ret.bytes;
916                         } ();
917                         return;
918                     }
919                     else
920                     {
921                         _storage_ = () inout {
922                             mixin(`inout _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
923                             return ret;
924                         } ();
925                         return;
926                     }
927                 }
928             }
929         }
930         else
931         {
932             static if (allSatisfy!(hasMutableConstruction, AllowedTypes))
933             this(return ref scope Algebraic rhs)
934             {
935                 static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_;
936                 static foreach (int i, T; AllowedTypes)
937                 static if (!is(T == typeof(null)) && !is(T == void))
938                 {
939                     if (_identifier_ == i)
940                     {
941                         _storage_ = () {
942                             mixin(`_Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
943                             return ret;
944                         } ();
945                         return;
946                     }
947                 }
948             }
949 
950             static if (allSatisfy!(hasConstConstruction, AllowedTypes))
951             this(return ref scope const Algebraic rhs) const
952             {
953                 static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_;
954                 static foreach (int i, T; AllowedTypes)
955                 static if (!is(T == typeof(null)) && !is(T == void))
956                 {
957                     if (_identifier_ == i)
958                     {
959                         _storage_ = () const {
960                             mixin(`const _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
961                             return ret;
962                         } ();
963                         return;
964                     }
965                 }
966             }
967 
968             static if (allSatisfy!(hasImmutableConstruction, AllowedTypes))
969             this(return ref scope immutable Algebraic rhs) immutable
970             {
971                 static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_;
972                 static foreach (int i, T; AllowedTypes)
973                 static if (!is(T == typeof(null)) && !is(T == void))
974                 {
975                     if (_identifier_ == i)
976                     {
977                         _storage_ = () immutable {
978                             mixin(`immutable _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
979                             return ret;
980                         } ();
981                         return;
982                     }
983                 }
984             }
985 
986             static if (allSatisfy!(hasSemiImmutableConstruction, AllowedTypes))
987             this(return ref scope const Algebraic rhs) immutable
988             {
989                 static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_;
990                 static foreach (int i, T; AllowedTypes)
991                 static if (!is(T == typeof(null)) && !is(T == void))
992                 {
993                     if (_identifier_ == i)
994                     {
995                         _storage_ = () const {
996                             mixin(`immutable _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
997                             return ret;
998                         } ();
999                         return;
1000                     }
1001                 }
1002             }
1003 
1004             static if (allSatisfy!(hasSemiMutableConstruction, AllowedTypes))
1005             this(return ref scope const Algebraic rhs)
1006             {
1007                 static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_;
1008                 static foreach (int i, T; AllowedTypes)
1009                 static if (!is(T == typeof(null)) && !is(T == void))
1010                 {
1011                     if (_identifier_ == i)
1012                     {
1013                         _storage_ = () const {
1014                             mixin(`const _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`);
1015                             return ret;
1016                         } ();
1017                         return;
1018                     }
1019                 }
1020             }
1021         }
1022     }
1023 
1024     /++
1025     +/
1026     size_t toHash() @trusted nothrow const
1027     {
1028         static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null))))
1029         {
1030             return 0;
1031         }
1032         else
1033         switch (_identifier_)
1034         {
1035             static foreach (i, T; AllowedTypes)
1036             {
1037                 case i:
1038                     static if (is(T == void))
1039                         return i;
1040                     else
1041                     static if (is(T == typeof(null)))
1042                         return i;
1043                     else
1044                     static if (__traits(compiles, hashOf(trustedGet!T, cast(size_t)i)))
1045                         return hashOf(trustedGet!T, cast(size_t)i);
1046                     else
1047                     {
1048                         debug pragma(msg, "Mir warning: can't compute hash of " ~ (const T).stringof);
1049                         return i;
1050                     }
1051             }
1052             default: assert(0);
1053         }
1054     }
1055 
1056     /++
1057     +/
1058     bool opEquals()(auto ref const typeof(this) rhs) const @trusted
1059     {
1060         static if (AllowedTypes.length == 0)
1061         {
1062             return true;
1063         }
1064         else
1065         {
1066             if (this._identifier_ != rhs._identifier_)
1067                 return false;
1068             switch (_identifier_)
1069             {
1070                 static foreach (i, T; AllowedTypes)
1071                 {
1072                     case i:
1073                         return this.trustedGet!T == rhs.trustedGet!T;
1074                 }
1075                 default: assert(0);
1076             }
1077         }
1078     }
1079 
1080     /++
1081     +/
1082     static if (is(AllowedTypes == _Types))
1083     auto opCmp()(auto ref const typeof(this) rhs) const @trusted
1084     {
1085         static if (AllowedTypes.length == 0)
1086         {
1087             return 0;
1088         }
1089         else
1090         {
1091             import mir.internal.utility: isFloatingPoint;
1092             if (auto d = int(this._identifier_) - int(rhs._identifier_))
1093                 return d;
1094             switch (_identifier_)
1095             {
1096                 static foreach (i, T; AllowedTypes)
1097                 {
1098                     case i:
1099                         static if (__traits(compiles, __cmp(trustedGet!T, rhs.trustedGet!T)))
1100                             return __cmp(trustedGet!T, rhs.trustedGet!T);
1101                         else
1102                         static if (__traits(hasMember, T, "opCmp") && !is(T == U*, U))
1103                             return this.trustedGet!T.opCmp(rhs.trustedGet!T);
1104                         else
1105                         // static if (isFloatingPoint!T)
1106                         //     return trustedGet!T == rhs ? 0 : trustedGet!T - rhs.trustedGet!T;
1107                         // else
1108                             return this.trustedGet!T < rhs.trustedGet!T ? -1 :
1109                                 this.trustedGet!T > rhs.trustedGet!T ? +1 : 0;
1110                 }
1111                 default: assert(0);
1112             }
1113         }
1114     }
1115 
1116     /// Requires mir-algorithm package
1117     string toString()() const
1118     {
1119         static if (AllowedTypes.length == 0)
1120         {
1121             return "Algebraic";
1122         }
1123         else
1124         {
1125             import mir.conv: to;
1126             switch (_identifier_)
1127             {
1128                 static foreach (i, T; AllowedTypes)
1129                 {
1130                     case i:
1131                         static if (is(T == void))
1132                             return "void";
1133                         else
1134                         static if (is(T == typeof(null)))
1135                             return "null";
1136                         else
1137                         static if (__traits(compiles, { auto s = to!string(trustedGet!T);}))
1138                             return to!string(trustedGet!T);
1139                         else
1140                             return AllowedTypes[i].stringof;
1141                 }
1142                 default: assert(0);
1143             }
1144         }
1145     }
1146 
1147     ///ditto
1148     void toString(W)(scope ref W w) const
1149     {
1150         static if (AllowedTypes.length == 0)
1151         {
1152             return w.put("Algebraic");
1153         }
1154         else
1155         {
1156             switch (_identifier_)
1157             {
1158                 static foreach (i, T; AllowedTypes)
1159                 {
1160                     case i:
1161                         static if (is(T == void))
1162                             return w.put("void");
1163                         else
1164                         static if (is(T == typeof(null)))
1165                             return w.put("null");
1166                         else
1167                         static if (__traits(compiles, { import mir.format: print; print(w, trustedGet!T); }))
1168                             { import mir.format: print; print(w, trustedGet!T); }
1169                         else
1170                             w.put(AllowedTypes[i].stringof);
1171                         return;
1172                 }
1173                 default: assert(0);
1174             }
1175         }
1176     }
1177 
1178     static if (is(AllowedTypes[0] == typeof(null)))
1179     {
1180         ///
1181         bool opCast(C)() const
1182             if (is(C == bool))
1183         {
1184             return _identifier_ != 0;
1185         }
1186 
1187         ///
1188         Algebraic opCast(C)() const
1189             if (is(C == Algebraic))
1190         {
1191             return this;
1192         }
1193 
1194         /// Defined if the first type is `typeof(null)`
1195         bool isNull() const @property { return _identifier_ == 0; }
1196         /// ditto
1197         void nullify() { this = null; }
1198 
1199         /// ditto
1200         auto get()()
1201             if (allSatisfy!(isCopyable, AllowedTypes[1 .. $]) && AllowedTypes.length != 2)
1202         {
1203             import mir.utility: _expect;
1204             if (_expect(!_identifier_, false))
1205             {
1206                 throw variantNullException;
1207             }
1208             static if (AllowedTypes.length != 2)
1209             {
1210                 Algebraic!(_Types[1 .. $]) ret;
1211 
1212                 S: switch (_identifier_)
1213                 {
1214                     static foreach (i, T; AllowedTypes[1 .. $])
1215                     {
1216                         {
1217                             case i + 1:
1218                                 if (!hasElaborateCopyConstructor!T && !__ctfe)
1219                                     goto default;
1220                                 ret = this.trustedGet!T;
1221                                 break S;
1222                         }
1223                     }
1224                     default:
1225                         ret._storage_.bytes = this._storage_.bytes;
1226                         static if (ret.AllowedTypes.length > 1)
1227                             ret._identifier_ = cast(typeof(ret._identifier_))(this._identifier_ - 1);
1228                 }
1229                 return ret;
1230             }
1231         }
1232 
1233         static if (AllowedTypes.length == 2)
1234         {
1235             /++
1236             Gets the value if not null. If `this` is in the null state, and the optional
1237             parameter `fallback` was provided, it will be returned. Without `fallback`,
1238             calling `get` with a null state is invalid.
1239         
1240             When the fallback type is different from the Nullable type, `get(T)` returns
1241             the common type.
1242         
1243             Params:
1244                 fallback = the value to return in case the `Nullable` is null.
1245         
1246             Returns:
1247                 The value held internally by this `Nullable`.
1248             +/
1249             auto ref inout(AllowedTypes[1]) get() return inout
1250             {
1251                 assert(_identifier_, "Called `get' on null Nullable!(" ~ AllowedTypes[1].stringof ~ ").");
1252                 return trustedGet!(AllowedTypes[1]);
1253             }
1254 
1255             version(mir_core_test)
1256             static if (_variant_test_)
1257             ///
1258             @safe pure nothrow @nogc
1259             unittest
1260             {
1261                 enum E { a = "a", b = "b" }
1262                 Nullable!E f = E.a;
1263                 auto e = f.get();
1264                 static assert(is(typeof(e) == E), Nullable!E.AllowedTypes.stringof);
1265                 assert(e == E.a);
1266 
1267                 assert(f.get(E.b) == E.a);
1268 
1269                 f = null;
1270                 assert(f.get(E.b) == E.b);
1271             }
1272 
1273             /// ditto
1274             @property auto ref inout(AllowedTypes[1]) get()(auto ref inout(AllowedTypes[1]) fallback) return inout
1275             {
1276                 return isNull ? fallback : get();
1277             }
1278         }
1279     }
1280 
1281     /++
1282     Checks if the underlaying type is an element of a user provided type set.
1283     +/
1284     bool _is(R : Algebraic!RetTypes, RetTypes...)() @safe pure nothrow @nogc const @property
1285         if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes))
1286     {
1287         static if (is(RetTypes == _Types))
1288             return true;
1289         else
1290         {
1291             import std.meta: staticIndexOf;
1292             import std.traits: CopyTypeQualifiers;
1293             alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes;
1294             alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes);
1295             // uint rhsTypeId;
1296             switch (_identifier_)
1297             {
1298                 foreach (i, T; AllowedTypes)
1299                 static if (staticIndexOf!(T, RhsAllowedTypes) >= 0)
1300                 {
1301                     case i:
1302                         return true;
1303                 }
1304                 default:
1305                     return false;
1306             }
1307         }
1308     }
1309 
1310     /// ditto
1311     bool _is(RetTypes...)() @safe pure nothrow @nogc const @property
1312         if (RetTypes.length > 1)
1313     {
1314         return this._is!(Variant!RetTypes);
1315     }
1316 
1317     /++
1318     `nothrow` $(LREF .Algebraic.get) alternative that returns an algebraic subset.
1319     +/
1320     auto ref trustedGet(R : Algebraic!RetTypes, this This, RetTypes...)() return @property
1321         if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes))
1322     {
1323         static if (is(RetTypes == _Types))
1324             return this;
1325         else
1326         {
1327             import std.meta: staticIndexOf;
1328             import std.traits: CopyTypeQualifiers;
1329             alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes;
1330             alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes);
1331             // uint rhsTypeId;
1332             switch (_identifier_)
1333             {
1334                 foreach (i, T; AllowedTypes)
1335                 static if (staticIndexOf!(T, RhsAllowedTypes) >= 0)
1336                 {
1337                     case i:
1338                         static if (is(T == void))
1339                             return (()@trusted => cast(Ret) Ret._void)();
1340                         else
1341                             return Ret(trustedGet!T);
1342                 }
1343                 default:
1344                     assert(0, variantMemberExceptionMsg);
1345             }
1346         }
1347     }
1348 
1349     /// ditto
1350     template trustedGet(RetTypes...)
1351         if (RetTypes.length > 1)
1352     {
1353         ///
1354         auto ref trustedGet(this This)() return
1355         {
1356             return this.trustedGet!(Variant!RetTypes);
1357         }
1358     }
1359 
1360     version(mir_core_test)
1361     static if (_variant_test_)
1362     ///
1363     @safe pure nothrow @nogc
1364     unittest
1365     {
1366         alias Float = Variant!(float, double);
1367         alias Int = Variant!(long, int);
1368         alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes);
1369 
1370         Number number = 3.0;
1371         assert(number._is!Float);
1372         auto fp = number.trustedGet!Float;
1373         static assert(is(typeof(fp) == Float));
1374         assert(fp == 3.0);
1375 
1376         // type list overload
1377         number = 12L;
1378         assert(number._is!(int, long));
1379         auto integer = number.trustedGet!(int, long);
1380         static assert(is(typeof(integer) == Int));
1381         assert(integer == 12L);
1382     }
1383 
1384     static if (anySatisfy!(isTaggedType, _Types))
1385     /// `trustedGet` overload that accept $(LREF .Algebraic.Kind).
1386     alias trustedGet(Kind kind) = trustedGet!(AllowedTypes[kind]);
1387 
1388     /++
1389     Gets an algebraic subset.
1390 
1391     Throws: Exception if the storage contains value of the type that isn't represented in the allowed type set of the requested algebraic.
1392     +/
1393     auto ref get(R : Algebraic!RetTypes, this This, RetTypes...)() return @property
1394         if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes))
1395     {
1396         static if (is(RetTypes == _Types))
1397             return this;
1398         else
1399         {
1400             import std.meta: staticIndexOf;
1401             import std.traits: CopyTypeQualifiers;
1402             alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes;
1403             alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes);
1404             // uint rhsTypeId;
1405             switch (_identifier_)
1406             {
1407                 foreach (i, T; AllowedTypes)
1408                 static if (staticIndexOf!(T, RhsAllowedTypes) >= 0)
1409                 {
1410                     case i:
1411                         static if (is(T == void))
1412                             return (()@trusted => cast(Ret) Ret._void)();
1413                         else
1414                             return Ret(trustedGet!T);
1415                 }
1416                 default:
1417                     throw variantMemberException;
1418             }
1419         }
1420     }
1421 
1422     /// ditto
1423     template get(RetTypes...)
1424         if (RetTypes.length > 1)
1425     {
1426         ///
1427         auto ref get(this This)() return
1428         {
1429             return this.get!(Variant!RetTypes);
1430         }
1431     }
1432 
1433     version(mir_core_test)
1434     static if (_variant_test_)
1435     ///
1436     @safe pure @nogc
1437     unittest
1438     {
1439         alias Float = Variant!(float, double);
1440         alias Int = Variant!(long, int);
1441         alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes);
1442 
1443         Number number = 3.0;
1444         auto fp = number.get!Float;
1445         static assert(is(typeof(fp) == Float));
1446         assert(fp == 3.0);
1447 
1448         // type list overload
1449         number = 12L;
1450         auto integer = number.get!(int, long);
1451         static assert(is(typeof(integer) == Int));
1452         assert(integer == 12L);
1453     }
1454 
1455     static if (anySatisfy!(isTaggedType, _Types))
1456     /// `get` overload that accept $(LREF .Algebraic.Kind).
1457     alias get(Kind kind) = get!(AllowedTypes[kind]);
1458 
1459     private alias _ReflectionTypes = AllowedTypes[is(AllowedTypes[0] == typeof(null)) .. $];
1460 
1461     static if (_ReflectionTypes.length)
1462     this(this This, Args...)(auto ref Args args)
1463         if (Args.length && (Args.length > 1 || !isVariant!(Args[0])))
1464     {
1465         import std.traits: CopyTypeQualifiers;
1466         import core.lifetime: forward;
1467 
1468         template CanCompile(T)
1469         {
1470             alias Q = CopyTypeQualifiers!(This, T);
1471             enum CanCompile = __traits(compiles, new Q(forward!args));
1472         }
1473 
1474         alias TargetType = Filter!(CanCompile, _ReflectionTypes);
1475         static if (TargetType.length == 0)
1476             static assert(0, typeof(this).stringof ~ ".this: no types can be constructed with arguments " ~ Args.stringof);
1477         static assert(TargetType.length == 1, typeof(this).stringof ~ ".this: multiple types " ~ TargetType.stringof ~ " can be constructed with arguments " ~ Args.stringof);
1478         alias TT = TargetType[0];
1479         static if (is(TT == struct) || is(TT == union))
1480             this(CopyTypeQualifiers!(This, TT)(forward!args));
1481         else
1482             this(new CopyTypeQualifiers!(This, TT)(forward!args));
1483     }
1484 
1485     static if (_ReflectionTypes.length && allSatisfy!(isSimpleAggregateType, _ReflectionTypes))
1486     {
1487         static foreach (member; AllMembersRec!(_ReflectionTypes[0]))
1488         static if (
1489             member != "_ID_" &&
1490             member != "_identifier_" &&
1491             member != "_is" &&
1492             member != "_storage_" &&
1493             member != "_Storage_" &&
1494             member != "_variant_test_" &&
1495             member != "_void" &&
1496             member != "AllowedTypes" &&
1497             member != "get" &&
1498             member != "isNull" &&
1499             member != "kind" &&
1500             member != "Kind" &&
1501             member != "nullify" &&
1502             member != "opAssign" &&
1503             member != "opCast" &&
1504             member != "opCmp" &&
1505             member != "opEquals" &&
1506             member != "opPostMove" &&
1507             member != "toHash" &&
1508             member != "toString" &&
1509             member != "trustedGet" &&
1510             member != "deserializeFromAsdf" &&
1511             member != "deserializeFromIon" &&
1512             !(member.length >= 2 && member[0 .. 2] == "__"))
1513         static if (allSatisfy!(ApplyRight!(hasMember, member), _ReflectionTypes))
1514         static if (!anySatisfy!(ApplyRight!(isMemberType, member), _ReflectionTypes))
1515         static if (allSatisfy!(ApplyRight!(isSingleMember, member), _ReflectionTypes))
1516         static if (allSatisfy!(ApplyRight!(isPublic, member), _ReflectionTypes))
1517         {
1518             static if (allSatisfy!(ApplyRight!(hasField, member), _ReflectionTypes) && NoDuplicates!(staticMap!(ApplyRight!(memberTypeOf, member), _ReflectionTypes)).length == 1)
1519             {
1520                 mixin(`ref ` ~ member ~q{()() inout return @trusted pure nothrow @nogc @property { return this.getMember!member; }});
1521             }
1522             else
1523             static if (allSatisfy!(ApplyRight!(templateOr!(hasField, isProperty), member), _ReflectionTypes))
1524             {
1525                 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;  }});
1526             }
1527             static if (allSatisfy!(ApplyRight!(templateNot!(templateOr!(hasField, isProperty)), member), _ReflectionTypes))
1528             {
1529                 mixin(`auto ref ` ~ member ~q{(this This, Args...)(auto ref Args args) { static if (args.length) { import core.lifetime: forward; return this.getMember!member(forward!args); } else return this.getMember!member;  }});
1530             }
1531         }
1532     }
1533 
1534     ///
1535     ref opAssign(RhsTypes...)(Algebraic!RhsTypes rhs) return @trusted
1536         if (RhsTypes.length < AllowedTypes.length && allSatisfy!(Contains!AllowedTypes, Algebraic!RhsTypes.AllowedTypes))
1537     {
1538         import core.lifetime: forward;
1539         static if (anySatisfy!(hasElaborateDestructor, AllowedTypes))
1540             this.__dtor();
1541         __ctor(forward!rhs);
1542         return this;
1543     }
1544 
1545     static foreach (int i, T; AllowedTypes)
1546     {
1547         /// Zero cost always nothrow `get` alternative
1548         auto ref trustedGet(E)() @trusted @property return inout nothrow
1549             if (is(E == T))
1550         {
1551             assert (i == _identifier_);
1552             static if (is(T == typeof(null)))
1553                 return null;
1554             else
1555             static if (is(T == void))
1556                 return;
1557             else
1558                 return _storage_.payload[i];
1559         }
1560 
1561         /++
1562         Throws: Exception if the storage contains value of other type
1563         +/
1564         auto ref get(E)() @property return inout
1565             if (is(E == T))
1566         {
1567             import mir.utility: _expect;
1568             static if (AllowedTypes.length > 1)
1569             {
1570                 if (_expect(i != _identifier_, false))
1571                 {
1572                     throw variantException;
1573                 }
1574             }
1575             return trustedGet!T;
1576         }
1577 
1578         /++
1579         Checks if the storage stores an allowed type.
1580         +/
1581         bool _is(E)() const @property nothrow @nogc
1582             if (is(E == T))
1583         {
1584             return _identifier_ == i;
1585         }
1586 
1587         static if (is(T == void))
1588         {
1589             /// Defined if `AllowedTypes` contains `void`
1590             static Algebraic _void()
1591             {
1592                 Algebraic ret;
1593                 ret._storage_ = () {
1594                     import core.lifetime: forward;
1595                     mixin(`_Storage_ ret = { _member_` ~ i.stringof ~ ` : _Void!().init };`);
1596                     return ret;
1597                 } ();
1598                 ret._identifier_ = i;
1599                 return ret;
1600             }
1601         }
1602         else
1603         {
1604             ///
1605             static if (isCopyable!(const T) || is(Unqual!T == T))
1606             this(T value)
1607             {
1608                 import core.lifetime: forward;
1609                 static if (is(T == typeof(null)))
1610                     auto rhs = _Null!()();
1611                 else
1612                     alias rhs = forward!value;
1613 
1614                 static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
1615                 {
1616                     _storage_.bytes = () @trusted {
1617                         auto ret =  _StorageI!i(rhs);
1618                         return ret.bytes;
1619                     } ();
1620                 }
1621                 else
1622                 {
1623                     _storage_ = () {
1624                         mixin(`_Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs };`);
1625                         return ret;
1626                     } ();
1627                 }
1628                 static if (_Payload.length > 1)
1629                     _identifier_ = i;
1630             }
1631 
1632             /// ditto
1633             static if (isCopyable!(const T))
1634             this(const T value) const
1635             {
1636                 static if (is(T == typeof(null)))
1637                     auto rhs = _Null!()();
1638                 else
1639                     alias rhs = value;
1640                 static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
1641                 {
1642                     _storage_.bytes = () const @trusted {
1643                         auto ret =  const _StorageI!i(rhs);
1644                         return ret.bytes;
1645                     } ();
1646                 }
1647                 else
1648                 {
1649                     _storage_ = () {
1650                         mixin(`const _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs };`);
1651                         return ret;
1652                     } ();
1653                 }
1654                 static if (_Payload.length > 1)
1655                     _identifier_ = i;
1656             }
1657 
1658             /// ditto
1659             static if (isCopyable!(immutable T))
1660             this(immutable T value) immutable
1661             {
1662                 static if (is(T == typeof(null)))
1663                     auto rhs = _Null!()();
1664                 else
1665                     alias rhs = value;
1666                 static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes))
1667                 {
1668                     _storage_.bytes = () const @trusted {
1669                         auto ret = immutable  _StorageI!i(rhs);
1670                         return ret.bytes;
1671                     } ();
1672                 }
1673                 else
1674                 {
1675                     _storage_ = () {
1676                         mixin(`immutable _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs };`);
1677                         return ret;
1678                     } ();
1679                 }
1680                 static if (_Payload.length > 1)
1681                     _identifier_ = i;
1682             }
1683 
1684             static if (__traits(compiles, (ref T a, ref T b) { moveEmplace(a, b); }))
1685             ///
1686             ref opAssign(T rhs) return @trusted
1687             {
1688                 import core.lifetime: forward;
1689                 static if (anySatisfy!(hasElaborateDestructor, AllowedTypes))
1690                     this.__dtor();
1691                 __ctor(forward!rhs);
1692                 return this;
1693             }
1694 
1695             /++
1696             +/
1697             auto opEquals()(auto ref const T rhs) const
1698             {
1699                 static if (AllowedTypes.length > 1)
1700                     if (_identifier_ != i)
1701                         return false;
1702                 return trustedGet!T == rhs;
1703             } 
1704 
1705             /++
1706             +/
1707             auto opCmp()(auto ref const T rhs) const
1708             {
1709                 import mir.internal.utility: isFloatingPoint;
1710                 static if (AllowedTypes.length > 1)
1711                     if (auto d = int(_identifier_) - int(i))
1712                         return d;
1713                 static if (__traits(compiles, __cmp(trustedGet!T, rhs)))
1714                     return __cmp(trustedGet!T, rhs);
1715                 else
1716                 static if (__traits(hasMember, T, "opCmp") && !is(T == U*, U))
1717                     return trustedGet!T.opCmp(rhs);
1718                 else
1719                 static if (isFloatingPoint!T)
1720                     return trustedGet!T == rhs ? 0 : trustedGet!T - rhs;
1721                 else
1722                     return trustedGet!T < rhs ? -1 :
1723                         trustedGet!T > rhs ? +1 : 0;
1724             }
1725 
1726             static if (is(Unqual!T == bool))
1727             {
1728                 private alias contains = Contains!AllowedTypes;
1729                 static if (contains!long && !contains!int)
1730                 {
1731                     this(int value)
1732                     {
1733                         this(long(value));
1734                     }
1735 
1736                     this(int value) const
1737                     {
1738                         this(long(value));
1739                     }
1740 
1741                     this(int value) immutable
1742                     {
1743                         this(long(value));
1744                     }
1745 
1746                     ref opAssign(int rhs) return @trusted
1747                     {
1748                         return opAssign(long(rhs));
1749                     }
1750 
1751                     auto opEquals()(int rhs) const
1752                     {
1753                         return opEquals(long(rhs));
1754                     } 
1755 
1756                     auto opCmp()(int rhs) const
1757                     {
1758                         return opCmp(long(rhs));
1759                     }
1760                 }
1761 
1762                 static if (contains!ulong && !contains!uint)
1763                 {
1764                     this(uint value)
1765                     {
1766                         this(ulong(value));
1767                     }
1768 
1769                     this(uint value) const
1770                     {
1771                         this(ulong(value));
1772                     }
1773 
1774                     this(uint value) immutable
1775                     {
1776                         this(ulong(value));
1777                     }
1778 
1779                     ref opAssign(uint rhs) return @trusted
1780                     {
1781                         return opAssign(ulong(rhs));
1782                     }
1783 
1784                     auto opEquals()(uint rhs) const
1785                     {
1786                         return opEquals(ulong(rhs));
1787                     } 
1788 
1789                     auto opCmp()(uint rhs) const
1790                     {
1791                         return opCmp(ulong(rhs));
1792                     }
1793                 }
1794             }
1795         }
1796     }
1797 }
1798 
1799 /++
1800 Constructor and methods propagation.
1801 +/
1802 version(mir_core_test)
1803 unittest
1804 {
1805     static struct Base
1806     {
1807         double d;
1808     }
1809 
1810     static class C
1811     {
1812         // alias this members are supported 
1813         Base base;
1814         alias base this;
1815 
1816         int a;
1817         private string _b;
1818 
1819     @safe pure nothrow @nogc:
1820 
1821         string b() const @property { return _b; }
1822         void b(string b) @property { _b = b; }
1823 
1824         int retArg(int v) { return v; }
1825 
1826         this(int a, string b)
1827         {
1828             this.a = a;
1829             this._b = b;
1830         }
1831     }
1832 
1833     static struct S
1834     {
1835         string b;
1836         int a;
1837 
1838         double retArg(double v) { return v; }
1839 
1840         // alias this members are supported 
1841         Base base;
1842         alias base this;
1843     }
1844 
1845     static void inc(ref int a) { a++; }
1846 
1847     alias V = Nullable!(C, S); // or Variant!
1848 
1849     auto v = V(2, "str");
1850     assert(v._is!C);
1851     assert(v.a == 2);
1852     assert(v.b == "str");
1853     // members are returned by reference if possible
1854     inc(v.a);
1855     assert(v.a == 3);
1856     v.b = "s";
1857     assert(v.b == "s");
1858     // alias this members are supported 
1859     v.d = 10;
1860     assert(v.d == 10);
1861     // method call support
1862     assert(v.retArg(100)._is!int);
1863     assert(v.retArg(100) == 100);
1864 
1865     v = V("S", 5);
1866     assert(v._is!S);
1867     assert(v.a == 5);
1868     assert(v.b == "S");
1869     // members are returned by reference if possible
1870     inc(v.a);
1871     assert(v.a == 6);
1872     v.b = "s";
1873     assert(v.b == "s");
1874     // alias this members are supported 
1875     v.d = 15;
1876     assert(v.d == 15);
1877     // method call support
1878     assert(v.retArg(300)._is!double);
1879     assert(v.retArg(300) == 300.0);
1880 
1881 }
1882 
1883 // test CTFE
1884 unittest
1885 {
1886     struct S { string s;}
1887     alias V = Nullable!(double, S);
1888     enum a = V(1.9);
1889     static assert (a == 1.9);
1890     enum b = V(S("str"));
1891     static assert(b == S("str"));
1892     static auto foo(int r)
1893     {
1894         auto s = V(S("str"));
1895         s = r;
1896         return s;
1897     }
1898 
1899     static assert(foo(3) == 3);
1900     static auto bar(int r)
1901     {
1902         auto s = V(S("str"));
1903         s = r;
1904         return s.visit!((double d) => d, (_)=> 0.0)();
1905     }
1906     assert(bar(3) == 3);
1907     static assert(bar(3) == 3);
1908 
1909     static auto bar3(int r)
1910     {
1911         auto s = V(S("str"));
1912         s = r;
1913         return s.match!((double d) => d, (_)=> "3")();
1914     }
1915     assert(bar(3) == 3);
1916     static assert(bar(3) == 3);
1917 }
1918 
1919 @safe pure @nogc nothrow
1920 version(mir_core_test) unittest
1921 {
1922     import core.stdc..string: memcmp;
1923 
1924     static struct C(ubyte payloadSize, bool isPOD, bool hasToHash = true, bool hasOpEquals = true)
1925     {
1926         ubyte[payloadSize] _payload;
1927 
1928     const:
1929 
1930         static if (!isPOD)
1931         {
1932             this(this) {}
1933             ~this() {}
1934         }
1935 
1936     @safe pure nothrow @nogc:
1937 
1938 
1939     static if (hasToHash)
1940         size_t toHash() { return hashOf(_payload); }
1941 
1942     static if (hasOpEquals)
1943         auto opEquals(ref const typeof(this) rhs) @trusted { return memcmp(_payload.ptr, rhs._payload.ptr, _payload.length); }
1944         auto opCmp(ref const typeof(this) rhs) { return _payload == rhs._payload; }
1945     }
1946 
1947     static foreach (size1; [1, 2, 4, 8, 10, 16, 20])
1948     static foreach (size2; [1, 2, 4, 8, 10, 16, 20])
1949     static if (size1 != size2)
1950     static foreach (isPOD; [true, false])
1951     static foreach (hasToHash; [true, false])
1952     static foreach (hasOpEquals; [true, false])
1953     {{
1954         alias T = Variant!(
1955                 double,
1956                 C!(size1, isPOD, hasToHash, hasOpEquals),
1957                 C!(size2, isPOD, hasToHash, hasOpEquals));
1958         // static assert (__traits(compiles, T.init <= T.init));
1959     }}
1960 }
1961 
1962 // const propogation
1963 @safe pure nothrow @nogc
1964 version(mir_core_test) unittest
1965 {
1966     static struct S1 { immutable(ubyte)* value; }
1967     static struct C1 { immutable(uint)* value; }
1968 
1969     alias V = Variant!(S1, C1);
1970     const V v = S1();
1971     assert(v._is!S1);
1972     V w = v;
1973     w = v;
1974 
1975     immutable f = V(S1());
1976     auto t = immutable V(S1());
1977     // auto j = immutable V(t);
1978     // auto i = const V(t);
1979 }
1980 
1981 // ditto
1982 @safe pure nothrow @nogc
1983 version(mir_core_test) unittest
1984 {
1985     static struct S2 {
1986         uint* value;
1987         this(return ref scope const typeof(this) rhs) {}
1988         ref opAssign(typeof(this) rhs) return { return this; }
1989     }
1990     static struct C2 { const(uint)* value; }
1991 
1992     alias V = Variant!(S2, C2);
1993     const V v = S2();
1994     V w = v;
1995     w = S2();
1996     w = v;
1997     w = cast(const) V.init;
1998 
1999     const f = V(S2());
2000     auto t = const V(f);
2001 }
2002 
2003 @safe pure nothrow @nogc
2004 version(mir_core_test) unittest
2005 {
2006     static struct S3 {
2007         uint* value;
2008         this(return ref scope typeof(this) rhs) {}
2009         this(return ref scope const typeof(this) rhs) const {}
2010         this(return ref scope immutable typeof(this) rhs) immutable {}
2011     }
2012     static struct C3 { immutable(uint)* value; }
2013 
2014     S3 s;
2015     S3 r = s;
2016     r = s;
2017     r = S3.init;
2018 
2019     alias V = Variant!(S3, C3);
2020     V v = S3();
2021     V w = v;
2022     w = S3();
2023     w = V.init;
2024     w = v;
2025 
2026     immutable V e = S3();
2027     auto t = immutable V(S3());
2028     auto j = const V(t);
2029     auto h = t;
2030 
2031     immutable V l = C3();
2032     auto g = immutable V(C3());
2033 }
2034 
2035 @safe pure nothrow @nogc
2036 version(mir_core_test) unittest
2037 {
2038     static struct S4 {
2039         uint* value;
2040         this(return ref scope const typeof(this) rhs) pure immutable {}
2041     }
2042     static struct C4 { immutable(uint)* value; }
2043 
2044 
2045     S4 s;
2046     S4 r = s;
2047     r = s;
2048     r = S4.init;
2049 
2050     alias V = Variant!(S4, C4);
2051     V v = S4();
2052     V w = v;
2053     w = S4();
2054     w = V.init;
2055     w = v;
2056 
2057     {
2058         const V e = S4();
2059         const k = w;
2060         auto t = const V(k);
2061         auto j = immutable V(k);
2062     }
2063 
2064     immutable V e = S4();
2065     immutable k = w;
2066     auto t = immutable V(S4());
2067     auto j = const V(t);
2068     auto h = t;
2069 
2070     immutable V l = C4();
2071     import core.lifetime;
2072     auto g = immutable V(C4());
2073     immutable b = immutable V(s);
2074 }
2075 
2076 @safe pure nothrow @nogc
2077 version(mir_core_test) unittest
2078 {
2079     import core.lifetime: move;
2080 
2081     static struct S5 {
2082         immutable(uint)* value;
2083         this(return ref scope typeof(this) rhs) {}
2084         this(return ref scope const typeof(this) rhs) immutable {}
2085     }
2086     static struct C5 { immutable(uint)* value; }
2087 
2088     S5 s;
2089     S5 r = s;
2090     r = s;
2091     r = S5.init;
2092 
2093     alias V = Variant!(S5, C5);
2094     V v = S5();
2095     V w = v;
2096     w = S5();
2097     w = V.init;
2098     w = v;
2099 
2100     immutable V e = S5();
2101     immutable f = V(S5());
2102     immutable k = w;
2103     auto t = immutable V(S5());
2104     auto j = const V(t);
2105     auto h = t;
2106 
2107     immutable V l = C5();
2108     import core.lifetime;
2109     immutable n = w.move;
2110     auto g = immutable V(C5());
2111     immutable b = immutable V(s);
2112 }
2113 
2114 @safe pure nothrow @nogc
2115 version(mir_core_test) unittest
2116 {
2117     static struct S {
2118         uint* value;
2119         this(this) @safe pure nothrow @nogc {}
2120         // void opAssign(typeof(this) rhs) {}
2121     }
2122     static struct C { const(uint)* value; }
2123 
2124     S s;
2125     S r = s;
2126     r = s;
2127     r = S.init;
2128 
2129     alias V = Variant!(S, C);
2130     V v = S();
2131     V w = v;
2132     w = S();
2133     w = V.init;
2134     w = v;
2135 }
2136 
2137 /++
2138 Applies a delegate or function to the given Variant depending on the held type,
2139 ensuring that all types are handled by the visiting functions.
2140 +/
2141 alias visit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.compileTime, false);
2142 
2143 ///
2144 @safe pure @nogc nothrow
2145 version(mir_core_test) unittest
2146 {
2147     alias Number = Variant!(int, double);
2148 
2149     Number x = 23;
2150     Number y = 1.0;
2151 
2152     assert(x.visit!((int v) => true, (float v) => false));
2153     assert(y.visit!((int v) => false, (float v) => true));
2154 }
2155 
2156 ///
2157 @safe pure @nogc
2158 version(mir_core_test) unittest
2159 {
2160     alias Number = Nullable!(int, double);
2161 
2162     Number z = null; // default
2163     Number x = 23;
2164     Number y = 1.0;
2165 
2166     () nothrow {
2167         assert(x.visit!((int v) => true, (float v) => false));
2168         assert(y.visit!((int v) => false, (v) => true));
2169         assert(z.visit!((typeof(null) v) => true, (v) => false));
2170     } ();
2171 
2172     auto xx = x.get;
2173     static assert (is(typeof(xx) == Variant!(int, double)));
2174     assert(xx.visit!((int v) => v, (float v) => 0) == 23);
2175     assert(xx.visit!((ref v) => v) == 23);
2176 
2177     x = null;
2178     y.nullify;
2179 
2180     assert(x.isNull);
2181     assert(y.isNull);
2182     assert(z.isNull);
2183     assert(z == y);
2184 }
2185 
2186 /++
2187 Checks $(LREF .Algebraic.toString) and `void`
2188 $(LREF Algerbraic)`.toString` requries `mir-algorithm` package
2189 +/
2190 @safe pure nothrow version(mir_core_test) unittest
2191 {
2192     import mir.conv: to;
2193     enum MIR_ALGORITHM = __traits(compiles, { import mir.format; });
2194 
2195     alias visitorHandler = visit!(
2196         (typeof(null)) => "NULL",
2197         () => "VOID",
2198         (ref r) {r += 1;}, // returns void
2199     );
2200 
2201     alias secondOrderVisitorHandler = visit!(
2202         () => "SO VOID", // void => to "RV VOID"
2203         (str) => str, // string to => it self
2204     );
2205 
2206     alias V = Nullable!(void, int);
2207     static assert(is(V == Variant!(typeof(null), void, int)));
2208 
2209     V variant;
2210 
2211     assert(secondOrderVisitorHandler(visitorHandler(variant)) == "NULL");
2212     assert(variant.to!string == "null");
2213 
2214     variant = V._void;
2215     assert(variant._is!void);
2216     assert(is(typeof(variant.get!void()) == void));
2217 
2218     assert(secondOrderVisitorHandler(visitorHandler(variant)) == "VOID");
2219     assert(variant.to!string == "void");
2220 
2221     variant = 5;
2222 
2223     assert(secondOrderVisitorHandler(visitorHandler(variant)) == "SO VOID");
2224     assert(variant == 6);
2225     assert(variant.to!string == (MIR_ALGORITHM ? "6" : "int"));
2226 }
2227 
2228 /++
2229 Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions.
2230 Throws: Exception if `naryFun!visitors` can't be called with provided arguments
2231 +/
2232 alias tryVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.exception, false);
2233 
2234 ///
2235 @safe pure @nogc
2236 version(mir_core_test) unittest
2237 {
2238     alias Number = Variant!(int, double);
2239 
2240     Number x = 23;
2241 
2242     assert(x.tryVisit!((int v) => true));
2243 }
2244 
2245 /++
2246 Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions.
2247 Returns: nullable variant, null value is used if `naryFun!visitors` can't be called with provided arguments.
2248 +/
2249 alias optionalVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.nullable, false);
2250 
2251 ///
2252 @safe pure @nogc nothrow
2253 version(mir_core_test) unittest
2254 {
2255     static struct S { int a; }
2256 
2257     Variant!(S, double) variant;
2258 
2259     alias optionalVisitInst = optionalVisit!((ref value) => value + 0);
2260 
2261     // do nothing because of variant isn't initialized
2262     Nullable!double result = optionalVisitInst(variant);
2263     assert(result.isNull);
2264 
2265     variant = S(2);
2266     // do nothing because of lambda can't compile
2267     result = optionalVisitInst(variant);
2268     assert(result == null);
2269 
2270     variant = 3.0;
2271     result = optionalVisitInst(variant);
2272     assert (result == 3.0);
2273 }
2274 
2275 /++
2276 Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions.
2277 Returns: optionally nullable type, null value is used if `naryFun!visitors` can't be called with provided arguments.
2278 +/
2279 alias autoVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.auto_, false);
2280 
2281 
2282 /++
2283 Applies a delegate or function to the given arguments depending on the held type,
2284 ensuring that all types are handled by the visiting functions.
2285 
2286 The handler supports multiple dispatch or multimethods: a feature of handler in which
2287 a function or method can be dynamically dispatched based on the run time (dynamic) type or,
2288 in the more general case, some other attribute of more than one of its arguments.
2289 
2290 Fuses algebraic types on return.
2291 
2292 See_also: $(HTTPS en.wikipedia.org/wiki/Multiple_dispatch, Multiple dispatch)
2293 +/
2294 alias match(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.compileTime, true);
2295 
2296 ///
2297 version(mir_core_test)
2298 unittest
2299 {
2300     struct Asteroid { uint size; }
2301     struct Spaceship { uint size; }
2302     alias SpaceObject = Variant!(Asteroid, Spaceship);
2303 
2304     alias collideWith = match!(
2305         (Asteroid x, Asteroid y) => "a/a",
2306         (Asteroid x, Spaceship y) => "a/s",
2307         (Spaceship x, Asteroid y) => "s/a",
2308         (Spaceship x, Spaceship y) => "s/s",
2309     );
2310 
2311     import mir.utility: min;
2312 
2313     // Direct access of a member in case of all algebraic types has this member
2314     alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1;
2315 
2316     alias collide = (x, y) => oops(x, y) ? "big-boom" : collideWith(x, y);
2317 
2318     auto ea = Asteroid(1);
2319     auto es = Spaceship(2);
2320     auto oa = SpaceObject(ea);
2321     auto os = SpaceObject(es);
2322 
2323     // Asteroid-Asteroid
2324     assert(collide(ea, ea) == "a/a");
2325     assert(collide(ea, oa) == "a/a");
2326     assert(collide(oa, ea) == "a/a");
2327     assert(collide(oa, oa) == "a/a");
2328 
2329     // Asteroid-Spaceship
2330     assert(collide(ea, es) == "a/s");
2331     assert(collide(ea, os) == "a/s");
2332     assert(collide(oa, es) == "a/s");
2333     assert(collide(oa, os) == "a/s");
2334 
2335     // Spaceship-Asteroid
2336     assert(collide(es, ea) == "s/a");
2337     assert(collide(es, oa) == "s/a");
2338     assert(collide(os, ea) == "s/a");
2339     assert(collide(os, oa) == "s/a");
2340 
2341     // Spaceship-Spaceship
2342     assert(collide(es, es) == "big-boom");
2343     assert(collide(es, os) == "big-boom");
2344     assert(collide(os, es) == "big-boom");
2345     assert(collide(os, os) == "big-boom");
2346 }
2347 
2348 /++
2349 Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions.
2350 Throws: Exception if `naryFun!visitors` can't be called with provided arguments
2351 
2352 Fuses algebraic types on return.
2353 +/
2354 alias tryMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.exception, true);
2355 
2356 ///
2357 version(mir_core_test)
2358 unittest
2359 {
2360     import std.exception: assertThrown;
2361     struct Asteroid { uint size; }
2362     struct Spaceship { uint size; }
2363     alias SpaceObject = Variant!(Asteroid, Spaceship);
2364 
2365     alias collideWith = tryMatch!(
2366         (Asteroid x, Asteroid y) => "a/a",
2367         // No visitor for A/S pair 
2368         // (Asteroid x, Spaceship y) => "a/s",
2369         (Spaceship x, Asteroid y) => "s/a",
2370         (Spaceship x, Spaceship y) => "s/s",
2371     );
2372 
2373     import mir.utility: min;
2374     // Direct access of a member in case of all algebraic types has this member
2375     alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1;
2376 
2377     alias collide = (x, y) => oops(x, y) ? "big-boom" : collideWith(x, y);
2378 
2379     auto ea = Asteroid(1);
2380     auto es = Spaceship(2);
2381     auto oa = SpaceObject(ea);
2382     auto os = SpaceObject(es);
2383 
2384     // Asteroid-Asteroid
2385     assert(collide(ea, ea) == "a/a");
2386     assert(collide(ea, oa) == "a/a");
2387     assert(collide(oa, ea) == "a/a");
2388     assert(collide(oa, oa) == "a/a");
2389 
2390     // Asteroid-Spaceship
2391     assertThrown!Exception(collide(ea, es));
2392     assertThrown!Exception(collide(ea, os));
2393     assertThrown!Exception(collide(oa, es));
2394     assertThrown!Exception(collide(oa, os));
2395 
2396     // can deduce the type based on other return values
2397     static assert(is(typeof(collide(ea, os)) == string));
2398     static assert(is(typeof(collide(oa, es)) == string));
2399     static assert(is(typeof(collide(oa, os)) == string));
2400 
2401     // Also allows newer compilers to detect combinations which always throw an exception
2402     static if (is(typeof(collideWith(ea, es)) == noreturn))
2403     {
2404         static assert(is(typeof(collide(ea, es)) == string));
2405     }
2406     else
2407     {
2408         // not enough information to deduce the type from (ea, es) pair
2409         static assert(is(typeof(collide(ea, es)) == void));
2410     }
2411 
2412     // Spaceship-Asteroid
2413     assert(collide(es, ea) == "s/a");
2414     assert(collide(es, oa) == "s/a");
2415     assert(collide(os, ea) == "s/a");
2416     assert(collide(os, oa) == "s/a");
2417 
2418     // Spaceship-Spaceship
2419     assert(collide(es, es) == "big-boom");
2420     assert(collide(es, os) == "big-boom");
2421     assert(collide(os, es) == "big-boom");
2422     assert(collide(os, os) == "big-boom");
2423 }
2424 
2425 /++
2426 Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions.
2427 Returns: nullable variant, null value is used if `naryFun!visitors` can't be called with provided arguments.
2428 
2429 Fuses algebraic types on return.
2430 +/
2431 alias optionalMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.nullable, true);
2432 
2433 ///
2434 version(mir_core_test)
2435 unittest
2436 {
2437     struct Asteroid { uint size; }
2438     struct Spaceship { uint size; }
2439     alias SpaceObject = Variant!(Asteroid, Spaceship);
2440 
2441     alias collideWith = optionalMatch!(
2442         (Asteroid x, Asteroid y) => "a/a",
2443         // No visitor for A/S pair 
2444         // (Asteroid x, Spaceship y) => "a/s",
2445         (Spaceship x, Asteroid y) => "s/a",
2446         (Spaceship x, Spaceship y) => "s/s",
2447     );
2448 
2449     import mir.utility: min;
2450     // Direct access of a member in case of all algebraic types has this member
2451     alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1;
2452 
2453     alias collide = (x, y) => oops(x, y) ? "big-boom".nullable : collideWith(x, y);
2454 
2455     auto ea = Asteroid(1);
2456     auto es = Spaceship(2);
2457     auto oa = SpaceObject(ea);
2458     auto os = SpaceObject(es);
2459 
2460     // Asteroid-Asteroid
2461     assert(collide(ea, ea) == "a/a");
2462     assert(collide(ea, oa) == "a/a");
2463     assert(collide(oa, ea) == "a/a");
2464     assert(collide(oa, oa) == "a/a");
2465 
2466     // Asteroid-Spaceship
2467     // assert(collide(ea, es).isNull);  // Compiler error: incompatible types
2468     assert(collideWith(ea, es).isNull); // OK
2469     assert(collide(ea, os).isNull);
2470     assert(collide(oa, es).isNull);
2471     assert(collide(oa, os).isNull);
2472 
2473 
2474     // Spaceship-Asteroid
2475     assert(collide(es, ea) == "s/a");
2476     assert(collide(es, oa) == "s/a");
2477     assert(collide(os, ea) == "s/a");
2478     assert(collide(os, oa) == "s/a");
2479 
2480     // Spaceship-Spaceship
2481     assert(collide(es, es) == "big-boom");
2482     assert(collide(es, os) == "big-boom");
2483     assert(collide(os, es) == "big-boom");
2484     assert(collide(os, os) == "big-boom");
2485 
2486     // check types  
2487 
2488     static assert(!__traits(compiles, collide(Asteroid.init, Spaceship.init)));
2489     static assert(is(typeof(collideWith(Asteroid.init, Spaceship.init)) == Nullable!()));
2490 
2491     static assert(is(typeof(collide(Asteroid.init, Asteroid.init)) == Nullable!string));
2492     static assert(is(typeof(collide(Asteroid.init, SpaceObject.init)) == Nullable!string));
2493     static assert(is(typeof(collide(SpaceObject.init, Asteroid.init)) == Nullable!string));
2494     static assert(is(typeof(collide(SpaceObject.init, SpaceObject.init)) == Nullable!string));
2495     static assert(is(typeof(collide(SpaceObject.init, Spaceship.init)) == Nullable!string));
2496     static assert(is(typeof(collide(Spaceship.init, Asteroid.init)) == Nullable!string));
2497     static assert(is(typeof(collide(Spaceship.init, SpaceObject.init)) == Nullable!string));
2498     static assert(is(typeof(collide(Spaceship.init, Spaceship.init)) == Nullable!string));
2499 }
2500 
2501 /++
2502 Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions.
2503 Returns: optionally nullable type, null value is used if `naryFun!visitors` can't be called with provided arguments.
2504 
2505 Fuses algebraic types on return.
2506 +/
2507 alias autoMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.auto_, true);
2508 
2509 ///
2510 version(mir_core_test)
2511 unittest
2512 {
2513     struct Asteroid { uint size; }
2514     struct Spaceship { uint size; }
2515     alias SpaceObject = Variant!(Asteroid, Spaceship);
2516 
2517     alias collideWith = autoMatch!(
2518         (Asteroid x, Asteroid y) => "a/a",
2519         // No visitor for A/S pair 
2520         // (Asteroid x, Spaceship y) => "a/s",
2521         (Spaceship x, Asteroid y) => "s/a",
2522         (Spaceship x, Spaceship y) => "s/s",
2523     );
2524 
2525     import mir.utility: min;
2526     // Direct access of a member in case of all algebraic types has this member
2527     alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1;
2528 
2529     import mir.conv: to;
2530     alias collide = (x, y) => oops(x, y) ? "big-boom".to!(typeof(collideWith(x, y))) : collideWith(x, y);
2531 
2532     auto ea = Asteroid(1);
2533     auto es = Spaceship(2);
2534     auto oa = SpaceObject(ea);
2535     auto os = SpaceObject(es);
2536 
2537     // Asteroid-Asteroid
2538     assert(collide(ea, ea) == "a/a");
2539     assert(collide(ea, oa) == "a/a");
2540     assert(collide(oa, ea) == "a/a");
2541     assert(collide(oa, oa) == "a/a");
2542 
2543     // Asteroid-Spaceship
2544     // assert(collide(ea, es).isNull);  // Compiler error: incompatible types
2545     assert(collideWith(ea, es).isNull); // OK
2546     assert(collide(ea, os).isNull);
2547     assert(collide(oa, es).isNull);
2548     assert(collide(oa, os).isNull);
2549 
2550     // Spaceship-Asteroid
2551     assert(collide(es, ea) == "s/a");
2552     assert(collide(es, oa) == "s/a");
2553     assert(collide(os, ea) == "s/a");
2554     assert(collide(os, oa) == "s/a");
2555 
2556     // Spaceship-Spaceship
2557     assert(collide(es, es) == "big-boom");
2558     assert(collide(es, os) == "big-boom");
2559     assert(collide(os, es) == "big-boom");
2560     assert(collide(os, os) == "big-boom");
2561 
2562     // check types  
2563 
2564     static assert(!__traits(compiles, collide(Asteroid.init, Spaceship.init)));
2565     static assert(is(typeof(collideWith(Asteroid.init, Spaceship.init)) == Nullable!()));
2566 
2567     static assert(is(typeof(collide(Asteroid.init, Asteroid.init)) == string));
2568     static assert(is(typeof(collide(SpaceObject.init, Asteroid.init)) == string));
2569     static assert(is(typeof(collide(Spaceship.init, Asteroid.init)) == string));
2570     static assert(is(typeof(collide(Spaceship.init, SpaceObject.init)) == string));
2571     static assert(is(typeof(collide(Spaceship.init, Spaceship.init)) == string));
2572 
2573     static assert(is(typeof(collide(Asteroid.init, SpaceObject.init)) == Nullable!string));
2574     static assert(is(typeof(collide(SpaceObject.init, SpaceObject.init)) == Nullable!string));
2575     static assert(is(typeof(collide(SpaceObject.init, Spaceship.init)) == Nullable!string));
2576 }
2577 
2578 /++
2579 Applies a member handler to the given Variant depending on the held type,
2580 ensuring that all types are handled by the visiting handler.
2581 +/
2582 alias getMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.compileTime, false);
2583 
2584 ///
2585 @safe pure @nogc nothrow
2586 version(mir_core_test) unittest
2587 {
2588     static struct S { auto bar(int a) { return a; } enum boolean = true; }
2589     static struct C { alias bar = (double a) => a * 2; enum boolean = false; }
2590 
2591     alias V = Variant!(S, C);
2592 
2593     V x = S();
2594     V y = C();
2595 
2596     static assert(is(typeof(x.getMember!"bar"(2)) == Variant!(int, double)));
2597     assert(x.getMember!"bar"(2) == 2);
2598     assert(y.getMember!"bar"(2) != 4);
2599     assert(y.getMember!"bar"(2) == 4.0);
2600 
2601     // direct implementation
2602     assert(x.bar(2) == 2);
2603     assert(y.bar(2) != 4);
2604     assert(y.bar(2) == 4.0);
2605     assert(x.boolean);
2606     assert(!y.boolean);
2607 }
2608 
2609 /++
2610 Applies a member handler to the given Variant depending on the held type,
2611 ensuring that all types are handled by the visiting handler.
2612 
2613 Fuses algebraic types on return.
2614 +/
2615 alias matchMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.compileTime, true);
2616 
2617 ///
2618 @safe pure @nogc nothrow
2619 version(mir_core_test) unittest
2620 {
2621     static struct S
2622     {
2623         Nullable!int m;
2624     }
2625 
2626     static struct C
2627     {
2628         Variant!(float, double) m;
2629     }
2630 
2631     alias V = Variant!(S, C);
2632 
2633     V x = S(2.nullable);
2634     V y = C(Variant!(float, double)(4.0));
2635 
2636     // getMember returns an algebraic of algebaics
2637     static assert(is(typeof(x.getMember!"m") == Variant!(Variant!(float, double), Nullable!int)));
2638     // matchMember returns a fused algebraic
2639     static assert(is(typeof(x.matchMember!"m") == Nullable!(int, float, double)));
2640     assert(x.matchMember!"m" == 2);
2641     assert(y.matchMember!"m" != 4);
2642     assert(y.matchMember!"m" == 4.0);
2643 }
2644 
2645 /++
2646 Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
2647 Throws: Exception if member can't be accessed with provided arguments
2648 +/
2649 alias tryGetMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.exception, false);
2650 
2651 ///
2652 @safe pure @nogc
2653 version(mir_core_test) unittest
2654 {
2655     static struct S { int bar(int a) { return a; }}
2656     static struct C { alias Bar = (double a) => a * 2; }
2657 
2658     alias V = Variant!(S, C);
2659 
2660     V x = S();
2661     V y = C();
2662 
2663     static assert(is(typeof(x.tryGetMember!"bar"(2)) == int));
2664     static assert(is(typeof(y.tryGetMember!"Bar"(2)) == double));
2665     assert(x.tryGetMember!"bar"(2) == 2);
2666     assert(y.tryGetMember!"Bar"(2) == 4.0);
2667 }
2668 
2669 ///
2670 @safe pure @nogc nothrow
2671 version(mir_core_test) unittest
2672 {
2673     alias Number = Variant!(int, double);
2674 
2675     Number x = Number(23);
2676     Number y = Number(1.0);
2677 
2678     assert(x.visit!((int v) => true, (float v) => false));
2679     assert(y.visit!((int v) => false, (float v) => true));
2680 }
2681 
2682 /++
2683 Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
2684 Throws: Exception if member can't be accessed with provided arguments
2685 
2686 Fuses algebraic types on return.
2687 +/
2688 alias tryMatchMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.exception, true);
2689 
2690 /++
2691 Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
2692 Returns: nullable variant, null value is used if the member can't be called with provided arguments.
2693 +/
2694 alias optionalGetMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.nullable, false);
2695 
2696 /++
2697 Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
2698 Returns: nullable variant, null value is used if the member can't be called with provided arguments.
2699 
2700 Fuses algebraic types on return.
2701 +/
2702 alias optionalMatchMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.nullable, true);
2703 
2704 /++
2705 Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
2706 Returns: optionally nullable type, null value is used if the member can't be called with provided arguments.
2707 +/
2708 alias autoGetMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.auto_, false);
2709 
2710 /++
2711 Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor.
2712 Returns: optionally nullable type, null value is used if the member can't be called with provided arguments.
2713 
2714 Fuses algebraic types on return.
2715 +/
2716 alias autoMatchMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.auto_, true);
2717 
2718 private template getMemberHandler(string member)
2719 {
2720     ///
2721     auto ref getMemberHandler(V, Args...)(ref V value, auto ref Args args)
2722     {
2723         static if (Args.length == 0)
2724         {
2725             return __traits(getMember, value, member);
2726         }
2727         else
2728         {
2729             import core.lifetime: forward;
2730             import mir.reflection: hasField;
2731             static if (hasField!(V, member) && Args.length == 1)
2732                 return __traits(getMember, value, member) = forward!args;
2733             else
2734                 return __traits(getMember, value, member)(forward!args);
2735         }
2736     }
2737 }
2738 
2739 private template VariantReturnTypes(T...)
2740 {
2741     import std.meta: staticMap;
2742 
2743     alias VariantReturnTypes = NoDuplicates!(staticMap!(TryRemoveConst, T));
2744 }
2745 
2746 private enum Exhaustive
2747 {
2748     compileTime,
2749     exception,
2750     nullable,
2751     auto_,
2752 }
2753 
2754 private template nextVisitor(T, alias visitor, alias arg)
2755 {
2756     static if (is(T == void))
2757     {
2758         alias nextVisitor = visitor;
2759     }
2760     else
2761     auto ref nextVisitor(NextArgs...)(auto ref NextArgs nextArgs)
2762     {
2763         import core.lifetime: forward;
2764         return visitor(arg.trustedGet!T, forward!nextArgs);
2765     }
2766 }
2767 
2768 private template nextVisitor(alias visitor, alias arg)
2769 {
2770     auto ref nextVisitor(NextArgs...)(auto ref NextArgs nextArgs)
2771     {
2772         import core.lifetime: forward;
2773         return visitor(forward!arg, forward!nextArgs);
2774     }
2775 }
2776 
2777 private template visitThis(alias visitor, Exhaustive nextExhaustive, args...)
2778 {
2779     auto ref visitThis(T, Args...)(auto ref Args args)
2780     {
2781         import core.lifetime: forward;
2782         return .visitImpl!(nextVisitor!(T, visitor, forward!(args[0])), nextExhaustive, true)(forward!(args[1 .. $]));
2783     }
2784 }
2785 
2786 private template visitLast(alias visitor, args...)
2787 {
2788     auto ref visitLast(T, Args...)(auto ref Args args)
2789     {
2790         import core.lifetime: forward;
2791         static if (is(T == void))
2792             return visitor(forward!(args[1 .. $]));
2793         else
2794             return visitor(args[0].trustedGet!T, forward!(args[1 .. $]));
2795     }
2796 }
2797 
2798 private template visitImpl(alias visitor, Exhaustive exhaustive, bool fused)
2799 {
2800     import std.meta: anySatisfy, staticMap, AliasSeq;
2801 
2802     ///
2803     auto ref visitImpl(Args...)(auto ref Args args)
2804     {
2805         import core.lifetime: forward;
2806 
2807         static if (!anySatisfy!(isVariant, Args))
2808         {
2809             static if (exhaustive == Exhaustive.compileTime)
2810             {
2811                 return visitor(forward!args);
2812             }
2813             else
2814             static if (exhaustive == Exhaustive.exception)
2815             {
2816                 static if (__traits(compiles, visitor(forward!args)))
2817                     return visitor(forward!args);
2818                 else
2819                     throw variantMemberException;
2820             }
2821             else
2822             static if (exhaustive == Exhaustive.nullable)
2823             {
2824                 static if (__traits(compiles, visitor(forward!args)))
2825                     return Nullable!(typeof(visitor(forward!args)))(visitor(forward!args));
2826                 else
2827                     return Nullable!().init;
2828             }
2829             else
2830             static if (exhaustive == Exhaustive.auto_)
2831             {
2832                 static if (__traits(compiles, visitor(forward!args)))
2833                     return visitor(forward!args);
2834                 else
2835                     return Nullable!().init;
2836             }
2837             else
2838             static assert(0, "not implemented");
2839         }
2840         else
2841         static if (!isVariant!(Args[0]))
2842         {
2843             return .visitImpl!(nextVisitor!(visitor, args[0]), exhaustive, fused)(forward!(args[1 .. $]));
2844         }
2845         else
2846         {
2847             static if (fused && anySatisfy!(isVariant, Args[1 .. $]))
2848             {
2849                 alias fun = visitThis!(visitor, exhaustive);
2850             }
2851             else
2852             {
2853                 static assert (isVariant!(Args[0]), "First argument should be a Mir Algebraic type");
2854                 alias fun = visitLast!visitor;
2855             }
2856 
2857             template VariantReturnTypesImpl(T)
2858             {
2859                 static if (__traits(compiles, fun!T(forward!args)))
2860                     static if (fused && is(typeof(fun!T(forward!args)) : Algebraic!Types, Types...))
2861                         alias VariantReturnTypesImpl = TryRemoveConst!(typeof(fun!T(forward!args))).AllowedTypes;
2862                     else
2863                     alias VariantReturnTypesImpl = AliasSeq!(TryRemoveConst!(typeof(fun!T(forward!args))));
2864                 else
2865                 static if (exhaustive == Exhaustive.auto_)
2866                     alias VariantReturnTypesImpl = AliasSeq!(typeof(null));
2867                 else
2868                     alias VariantReturnTypesImpl = AliasSeq!();
2869             }
2870 
2871             static if (exhaustive == Exhaustive.nullable)
2872                 alias AllReturnTypes = NoDuplicates!(typeof(null), staticMap!(VariantReturnTypesImpl, Args[0].AllowedTypes));
2873             else
2874                 alias AllReturnTypes = NoDuplicates!(staticMap!(VariantReturnTypesImpl, Args[0].AllowedTypes));
2875 
2876             switch (args[0]._identifier_)
2877             {
2878                 static foreach (i, T; Args[0].AllowedTypes)
2879                 {
2880                     case i:
2881                         static if (__traits(compiles, fun!T(forward!args)) || exhaustive == Exhaustive.compileTime && !is(T == typeof(null)))
2882                         {
2883                             static if (AllReturnTypes.length == 1)
2884                             {
2885                                 return fun!T(forward!args);
2886                             }
2887                             else
2888                             static if (is(VariantReturnTypesImpl!T == AliasSeq!void))
2889                             {
2890                                 fun!T(forward!args);
2891                                 return Variant!AllReturnTypes._void;
2892                             }
2893                             else
2894                             static if (is(typeof(fun!T(forward!args)) == Variant!AllReturnTypes))
2895                             {
2896                                 return fun!T(forward!args);
2897                             }
2898                             else
2899                             {
2900                                 return Variant!AllReturnTypes(fun!T(forward!args));
2901                             }
2902                         }
2903                         else
2904                         static if (exhaustive == Exhaustive.compileTime && is(T == typeof(null)))
2905                         {
2906                             assert(0, "Null " ~ Args[0].stringof);
2907                         }
2908                         else
2909                         static if (exhaustive == Exhaustive.nullable || exhaustive == Exhaustive.auto_)
2910                         {
2911                             return Variant!AllReturnTypes(null);
2912                         }
2913                         else
2914                         {
2915                             throw variantMemberException;
2916                         }
2917                 }
2918                 default: assert(0);
2919             }
2920         }
2921     }
2922 }
2923 
2924 private string enumKindText()(string[] strs)
2925 {
2926     auto r = "enum Kind {";
2927     foreach (s; strs)
2928     {
2929         r ~= s;
2930         r ~= ", ";
2931     }
2932     r ~= "}";
2933     return r;
2934 }
2935 
2936 @safe pure @nogc
2937 version(mir_core_test) unittest
2938 {
2939     static struct S { int a; }
2940 
2941     Variant!(S, double) variant;
2942     variant = 1.0;
2943     variant.tryVisit!((ref value, b) => value += b)(2);
2944     assert (variant.get!double == 3);
2945 
2946     alias fun = (ref value) {
2947         static if (is(typeof(value) == S))
2948             value.a += 2;
2949         else
2950            value += 2;
2951     };
2952 
2953     variant.tryVisit!fun;
2954     assert (variant.get!double == 5);
2955 
2956     variant = S(4);
2957     variant.tryVisit!fun;
2958     assert (variant.get!S.a == 6);
2959 
2960     alias D = Variant!(Variant!(S, double));
2961 }
2962 
2963 @safe pure @nogc
2964 version(mir_core_test) unittest
2965 {
2966     import std.meta: AliasSeq;
2967 
2968     static struct PODWithLongPointer {
2969         long* x;
2970         this(long l) pure
2971         {
2972             x = new long(l);
2973         }
2974 
2975     @property:
2976         long a() const {
2977             return x ? *x : 0;
2978         }
2979 
2980         void a(long l) {
2981             if (x) {
2982                 *x = l;
2983             } else {
2984                 x = new long(l);
2985             }
2986         }
2987     }
2988     static assert(is(TypeSet!(byte, immutable PODWithLongPointer) == AliasSeq!(byte, immutable PODWithLongPointer)));
2989 }
2990 
2991 private enum isSimpleAggregateType(T) = is(T == class) || is(T == struct) || is(T == union) || is(T == interface);
2992 
2993 unittest
2994 {
2995     static struct Test
2996     {
2997         alias Value = void;
2998     }
2999 
3000     alias B = Nullable!Test;
3001 }