1 /// Add "tagged value" to D for an effective use of std.variant.Algebraic.
2 module tag;
3 
4 /**
5 Wraps **T** with **tag_**.
6 */
7 public struct Tag(string tag_, T = void) {
8   /// tag
9   enum string tag = tag_;
10   static if(is(T == void)) {
11   } else {
12     /// internal value
13     public T value;
14     alias value this;
15 
16     public this(T value) {
17       this.value = value;
18     }
19   }
20 }
21 
22 ///
23 @system unittest {
24   import std.variant : Algebraic, visit;
25   alias Option(T) = Algebraic!(
26     Tag!("Some", T),
27     Tag!"None"
28   );
29   Option!int option;
30   
31   option = tag!"Some"(1);
32   assert(option.visit!(
33     (Tag!("Some", int) a) => Option!int(tag!"Some"(a + 1)),
34     (Tag!"None" a) => Option!int(a)
35   ) == tag!"Some"(2));
36 
37   option = tag!"None"();
38   assert(option.visit!(
39     (Tag!("Some", int) a) => Option!int(tag!"Some"(a + 1)),
40     (Tag!"None" a) => Option!int(a)
41   ) == tag!"None"());
42 }
43 
44 @safe unittest {
45   alias T = Tag!("Tag", int);
46   static assert(T.tag == "Tag");
47 
48   T tagged = 12;
49   assert(tagged == tag!"Tag"(12));
50   assert(tagged + 1 == 13);
51 }
52 
53 /// Gets the underlying type whitch a **Tag** wraps. If **T** is not a **Tag** it will alias itself to **T**.
54 template TaggedType(T) {
55   static if(is(T == Tag!(tag_, U), string tag_, U)) {
56     alias TaggedType = U;
57   } else {
58     alias TaggedType = T;
59   }
60 }
61 
62 ///
63 @safe unittest {
64   static assert(is(
65     TaggedType!(Tag!("Tag", int)) == int
66   ));
67   static assert(is(
68     TaggedType!int == int
69   ));
70 }
71 
72 /// Wraps **value** with **tag_**. 
73 public template tag(string tag_) {
74   /// ditto
75   public Tag!(tag_, T) tag(T)(T value) {
76     Tag!(tag_, T) temp;
77     temp.value = value;
78     return temp;
79   }
80 
81   @safe unittest {
82     assert(tag!"Some"(1) == 1);
83     assert(tag!"Some"(1) != tag!"Some"(114514));
84   }
85 
86   /// ditto
87   public pure @safe @nogc Tag!(tag_, void) tag() {
88     Tag!(tag_, void) temp;
89     return temp;
90   }
91 }
92 
93 ///
94 @safe unittest {
95   Tag!("Tag", int) tagged = tag!"Tag"(1);
96   Tag!"None" nil = tag!"None";
97 }