1 /** 2 * __ __ __ __ 3 * / / /_/ _____ \ \ /_/ 4 * _____/ / _____ __ ___ __ / ___ \ _____ / / __ __ ___ _____ 5 * / ___ / / ___ \ \ \_/__/ / / / _____/ / ___ \ / / / / / /_/ / / ___ \ 6 * / /__/ / / /__/ \ / _/ / / / /__/ / / _____/ / / / / / _/ / / / /__/ / 7 * \_____/ \_____/\_\ /_/ __ / / \_____/ / /__/ / \_\ /_/ /_/ /_/ _\___ / 8 * / /__/ / \_____/ / /__/ / 9 * \_____/ \_____/ 10 * 11 * A module for values with two possiblities 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.either; 18 19 import std.format : format; 20 import std.traits : fullyQualifiedName; 21 import darjeeling.maybe : Maybe; 22 import darjeeling.assertion; 23 24 /** 25 * A interface to represent a two possible value. 26 * 27 * Example: 28 * ----- 29 * auto either = Either!(string, int).right(33-4); 30 * if (either.isRight) 31 * { 32 * writeln(either.right()); 33 * } 34 * ----- 35 * Output: 36 * ----- 37 * 29 38 * ----- 39 */ 40 interface Either(TLeft, TRight) 41 { 42 /** 43 * Return true if $(D this) is the Right!(TLeft, TRight) instance, otherwise false. 44 */ 45 @property bool isRight() nothrow pure @safe; 46 47 /** 48 * Return true if $(D this) is the Left!(TLeft, TRight) instance, otherwise false. 49 */ 50 @property bool isLeft() nothrow pure @safe; 51 52 /** 53 * Return TLeft value if $(D this) is the Left!(TLeft, TRight) instance, otherwise throw Exception. 54 */ 55 TLeft left() pure @safe; 56 57 /** 58 * Return TRight value if $(D this) is the Right!(TLeft, TRight) instance, otherwise throw Exception. 59 */ 60 TRight right() pure @safe; 61 62 /** 63 * Return Just!TLeft value if $(D this) is the Left!(TLeft, TRight) instance, otherwise Nothing!TLeft value. 64 */ 65 Maybe!TLeft tryLeft() nothrow pure @safe; 66 67 /** 68 * Return Just!TRight value if $(D this) is the Right!(TLeft, TRight) instance, otherwise Nothing!TRight value. 69 */ 70 Maybe!TRight tryRight() nothrow pure @safe; 71 72 /** 73 * A factory method to create Right(TLeft, TRight) instance. 74 * 75 * Params: 76 * value = a TRight value to contain 77 * 78 * Returns: 79 * A Right!(TLeft, TRight) instance which has $(D_PARAM value). 80 */ 81 static Either!(TLeft, TRight) right(TRight value) nothrow pure @safe 82 { 83 return new Right!(TLeft, TRight)(value); 84 } 85 86 /** 87 * A factory method to create Left(TLeft, TRight) instance. 88 * 89 * Params: 90 * value = a TLeft value to contain 91 * 92 * Returns: 93 * A Left!(TLeft, TRight) instance which has $(D_PARAM value). 94 */ 95 static Either!(TLeft, TRight) left(TLeft value) nothrow pure @safe 96 { 97 return new Left!(TLeft, TRight)(value); 98 } 99 } 100 101 final class Left(TLeft, TRight) : Either!(TLeft, TRight) 102 { 103 TLeft value; 104 105 package this(TLeft value) nothrow pure @safe 106 { 107 this.value = value; 108 } 109 110 @property bool isRight() nothrow pure @safe 111 { 112 return !this.isLeft; 113 } 114 115 unittest 116 { 117 auto left = new Left!(string, int)("hoge"); 118 assert(!left.isRight); 119 } 120 121 @property bool isLeft() nothrow pure @safe 122 { 123 return true; 124 } 125 126 unittest 127 { 128 auto left = new Left!(Exception, string)(new Exception("fuga")); 129 assert(left.isLeft); 130 } 131 132 TLeft left() pure @safe 133 { 134 return this.value; 135 } 136 137 unittest 138 { 139 auto expected = "hoge"; 140 auto left = new Left!(string, int)(expected); 141 auto actual = left.left(); 142 assertEquals(expected, actual); 143 } 144 145 TRight right() pure @safe 146 { 147 throw new Exception("Invalid operation: Left(TLeft, TRight) cannot return its right value."); 148 } 149 150 unittest 151 { 152 auto left = new Left!(Exception, string)(new Exception("bar")); 153 auto ex = trap!(Left!(Exception, string), string)(left, function(Left!(Exception, string) l) => l.right()); 154 auto expected = "Invalid operation: Left(TLeft, TRight) cannot return its right value."; 155 assertEquals(expected, ex.msg); 156 } 157 158 Maybe!TLeft tryLeft() nothrow pure @safe 159 { 160 return Maybe!TLeft.just(this.value); 161 } 162 163 unittest 164 { 165 auto expected = "left"; 166 auto left = new Left!(string, int)(expected); 167 auto actual = left.tryLeft(); 168 assert(actual.isJust); 169 assertEquals(expected, actual.fromJust()); 170 } 171 172 Maybe!TRight tryRight() nothrow pure @safe 173 { 174 return Maybe!TRight.nothing(); 175 } 176 177 unittest 178 { 179 auto left = new Left!(Exception, string)(new Exception("LeFT")); 180 auto actual = left.tryRight(); 181 assert(!actual.isJust); 182 } 183 184 override string toString() 185 { 186 return format("Left!(%s, %s)(%s)", fullyQualifiedName!TLeft, fullyQualifiedName!TRight, this.value); 187 } 188 189 override bool opEquals(Object o) 190 { 191 if (typeid(o) == typeid(this)) 192 { 193 auto other = cast(Left!(TLeft, TRight))(o); 194 return this.value == other.value; 195 } 196 else 197 { 198 return false; 199 } 200 } 201 } 202 203 final class Right(TLeft, TRight) : Either!(TLeft, TRight) 204 { 205 TRight value; 206 207 package this(TRight value) nothrow pure @safe 208 { 209 this.value = value; 210 } 211 212 @property bool isRight() nothrow pure @safe 213 { 214 return true; 215 } 216 217 unittest 218 { 219 auto right = new Right!(string, int)(334); 220 assert(right.isRight); 221 } 222 223 @property bool isLeft() nothrow pure @safe 224 { 225 return !this.isRight; 226 } 227 228 unittest 229 { 230 auto right = new Right!(Exception, string)("right"); 231 assert(!right.isLeft); 232 } 233 234 TLeft left() pure @safe 235 { 236 throw new Exception("Invalid operation: Right(TLeft, TRight) cannot return its left value."); 237 } 238 239 unittest 240 { 241 auto right = new Right!(string, int)(42); 242 auto ex = trap!(Right!(string, int), string)(right, function(r) => r.left()); 243 auto expected = "Invalid operation: Right(TLeft, TRight) cannot return its left value."; 244 assertEquals(expected, ex.msg); 245 } 246 247 TRight right() pure @safe 248 { 249 return this.value; 250 } 251 252 unittest 253 { 254 auto expected = "That's right"; 255 auto right = new Right!(Exception, string)(expected); 256 auto actual = right.right(); 257 assertEquals(expected, actual); 258 } 259 260 Maybe!TLeft tryLeft() nothrow pure @safe 261 { 262 return Maybe!TLeft.nothing(); 263 } 264 265 unittest 266 { 267 auto right = new Right!(string, int)(-10); 268 auto actual = right.tryLeft(); 269 assert(!actual.isJust); 270 } 271 272 Maybe!TRight tryRight() nothrow pure @safe 273 { 274 return Maybe!TRight.just(this.value); 275 } 276 277 unittest 278 { 279 auto expected = "right"; 280 auto right = new Right!(Exception, string)(expected); 281 auto actual = right.tryRight(); 282 assert(actual.isJust); 283 assertEquals(expected, actual.fromJust()); 284 } 285 286 override string toString() 287 { 288 return format("Right!(%s, %s)(%s)", fullyQualifiedName!TLeft, fullyQualifiedName!TRight, this.value); 289 } 290 291 override bool opEquals(Object o) 292 { 293 if (typeid(o) == typeid(this)) 294 { 295 auto other = cast(Right!(TLeft, TRight))(o); 296 return this.value == other.value; 297 } 298 else 299 { 300 return false; 301 } 302 } 303 } 304 305 unittest 306 { 307 // Left(TLeft, TRight) 308 { 309 auto expected = "hoge"; 310 Either!(string, int) either = Either!(string, int).left(expected); 311 assert(either.isLeft); 312 assert(!either.isRight); 313 auto left = either.tryLeft(); 314 assert(left.isJust); 315 assertEquals(expected, left.fromJust()); 316 } 317 // Right(TLeft, TRight) 318 { 319 auto expected = 334; 320 Either!(string, int) either = Either!(string, int).right(expected); 321 assert(!either.isLeft); 322 assert(either.isRight); 323 auto right = either.tryRight(); 324 assert(right.isJust); 325 assertEquals(expected, right.fromJust()); 326 } 327 // Equality 328 { 329 { 330 auto either1 = Either!(string, int).left("hoge"); 331 auto either2 = Either!(string, int).left("hoge"); 332 assertEquals(either1, either2); 333 } 334 { 335 auto either1 = Either!(string, int).left("Left"); 336 auto either2 = Either!(string, int).left("left"); 337 assertNotEquals(either1, either2); 338 } 339 { 340 auto either1 = Either!(string, int).left("fuga"); 341 auto either2 = Either!(Exception, string).left(new Exception("fuga")); 342 assertNotObjEquals(either1, either2); 343 } 344 { 345 auto either1 = Either!(string, int).right(42); 346 auto either2 = Either!(string, int).right(42); 347 assertEquals(either1, either2); 348 } 349 { 350 auto either1 = Either!(string, int).right(33); 351 auto either2 = Either!(string, int).right(4); 352 assertNotEquals(either1, either2); 353 } 354 { 355 auto either1 = Either!(string, int).right(10); 356 auto either2 = Either!(Exception, int).right(10); 357 assertNotObjEquals(either1, either2); 358 } 359 { 360 auto either1 = Either!(string, int).right(10); 361 auto either2 = Either!(string, int).left("ten"); 362 assertNotObjEquals(either1, either2); 363 } 364 { 365 auto either1 = Either!(string, int).left("test"); 366 auto either2 = Either!(Exception, string).right("test"); 367 assertNotObjEquals(either1, either2); 368 } 369 } 370 }