1 /** 2 * __ __ __ __ 3 * / / /_/ _____ \ \ /_/ 4 * _____/ / _____ __ ___ __ / ___ \ _____ / / __ __ ___ _____ 5 * / ___ / / ___ \ \ \_/__/ / / / _____/ / ___ \ / / / / / /_/ / / ___ \ 6 * / /__/ / / /__/ \ / _/ / / / /__/ / / _____/ / / / / / _/ / / / /__/ / 7 * \_____/ \_____/\_\ /_/ __ / / \_____/ / /__/ / \_\ /_/ /_/ /_/ _\___ / 8 * / /__/ / \_____/ / /__/ / 9 * \_____/ \_____/ 10 * 11 * A module for optional values 12 * 13 * Copyright: (C) Kazuhiro Matsushima 2016. 14 * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. 15 * Authors: Kazuhiro Matsushima 16 */ 17 module darjeeling.maybe; 18 19 import std.format : format; 20 import std.traits : fullyQualifiedName; 21 import darjeeling.assertion; 22 23 /** 24 * A interface to represent an optional value. 25 * 26 * Example: 27 * ----- 28 * auto maybe = Maybe!int.just(42); 29 * if (maybe.isJust) 30 * { 31 * writeln(maybe.fromJust()); 32 * } 33 * ----- 34 * Output: 35 * ----- 36 * 42 37 * ----- 38 */ 39 interface Maybe(T) 40 { 41 /** 42 * Return true if $(D this) is the Just!T instance, otherwise false. 43 */ 44 @property bool isJust() nothrow pure @safe; 45 46 /** 47 * Return true if $(D this) is the Nothing!T instance, otherwise false. 48 */ 49 @property bool isNothing() nothrow pure @safe; 50 51 /** 52 * Return $(D value) if $(D this) is the Just!T instance, otherwise throw Exception. 53 */ 54 T fromJust() pure @safe; 55 56 /** 57 * A factory method to create Just(T) instance. 58 * 59 * Params: 60 * value = a T value to contain 61 * 62 * Returns: 63 * A Just!T instance which has $(D_PARAM value). 64 */ 65 static Maybe!T just(T value) nothrow pure @safe 66 { 67 return new Just!T(value); 68 } 69 70 /** 71 * A factory method to create Nothing(T) instance. 72 * 73 * Returns: 74 * A Nothing!T instance. 75 */ 76 static Maybe!T nothing() nothrow pure @safe 77 { 78 return new Nothing!T(); 79 } 80 } 81 82 final class Just(T) : Maybe!T 83 { 84 private T value; 85 86 package this(T value) nothrow pure @safe 87 { 88 this.value = value; 89 } 90 91 @property bool isJust() nothrow pure @safe 92 { 93 return true; 94 } 95 96 unittest 97 { 98 auto just = new Just!int(2); 99 assert(just.isJust); 100 } 101 102 @property bool isNothing() nothrow pure @safe 103 { 104 return !this.isJust; 105 } 106 107 unittest 108 { 109 auto just = new Just!string("hoge"); 110 assert(!just.isNothing); 111 } 112 113 T fromJust() nothrow pure @safe 114 { 115 return this.value; 116 } 117 118 unittest 119 { 120 auto expected = 3; 121 auto just = new Just!int(expected); 122 auto actual = just.fromJust(); 123 assertEquals(expected, actual); 124 } 125 126 override string toString() 127 { 128 return format("Just!%s(%s)", fullyQualifiedName!T, this.value); 129 } 130 131 override bool opEquals(Object o) 132 { 133 if (typeid(o) == typeid(this)) 134 { 135 auto other = cast(Just!T)(o); 136 return this.value == other.value; 137 } 138 else 139 { 140 return false; 141 } 142 } 143 } 144 145 final class Nothing(T) : Maybe!T 146 { 147 package this() nothrow pure @safe {} 148 149 @property bool isJust() nothrow pure @safe 150 { 151 return !this.isNothing(); 152 } 153 154 unittest 155 { 156 auto nothing = new Nothing!string(); 157 assert(!nothing.isJust); 158 } 159 160 @property bool isNothing() nothrow pure @safe 161 { 162 return true; 163 } 164 165 unittest 166 { 167 auto nothing = new Nothing!int(); 168 assert(nothing.isNothing); 169 } 170 171 T fromJust() pure @safe 172 { 173 throw new Exception("Invalid operation: Nothing(T) cannot return its value."); 174 } 175 176 unittest 177 { 178 auto nothing = new Nothing!string(); 179 auto ex = trap!(Nothing!string, string)(nothing, function(Nothing!string n) => n.fromJust()); 180 auto expected = "Invalid operation: Nothing(T) cannot return its value."; 181 assertEquals(expected, ex.msg); 182 } 183 184 override string toString() 185 { 186 return format("Nothing!%s", fullyQualifiedName!T); 187 } 188 189 override bool opEquals(Object o) 190 { 191 return (typeid(o) == typeid(this)); 192 } 193 } 194 195 unittest 196 { 197 // Just(T) 198 { 199 auto expected = 2; 200 Maybe!int just = Maybe!int.just(expected); 201 assert(just.isJust); 202 assert(!just.isNothing); 203 auto actual = just.fromJust(); 204 assertEquals(expected, actual); 205 } 206 // Nothing(T) 207 { 208 Maybe!string nothing = Maybe!string.nothing(); 209 assert(!nothing.isJust); 210 assert(nothing.isNothing); 211 } 212 // Equality 213 { 214 { 215 auto maybe1 = Maybe!int.just(1); 216 auto maybe2 = Maybe!int.just(1); 217 assertEquals(maybe1, maybe2); 218 } 219 { 220 auto maybe1 = Maybe!string.just("hoge"); 221 auto maybe2 = Maybe!string.just("fuga"); 222 assertNotEquals(maybe1, maybe2); 223 } 224 { 225 auto maybe1 = Maybe!short.just(1); 226 auto maybe2 = Maybe!long.just(1); 227 assertNotObjEquals(maybe1, maybe2); 228 } 229 { 230 auto maybe1 = Maybe!int.just(1); 231 auto maybe2 = Maybe!int.nothing(); 232 assertNotEquals(maybe1, maybe2); 233 } 234 { 235 auto maybe1 = Maybe!string.nothing(); 236 auto maybe2 = Maybe!int.nothing(); 237 assertNotObjEquals(maybe1, maybe2); 238 } 239 { 240 auto maybe1 = Maybe!double.nothing(); 241 auto maybe2 = Maybe!double.nothing(); 242 assertEquals(maybe1, maybe2); 243 } 244 } 245 }