1 module either; 2 3 import std.traits; 4 import std.conv; 5 6 version(unittest) { 7 import fluent.asserts; 8 } 9 10 /// 11 enum EitherSide { 12 Left, Right 13 } 14 15 /// 16 bool isCallableWith(func, T)() { 17 static if(!isCallable!func) { 18 return false; 19 } else static if(Parameters!func.length != 1) { 20 return false; 21 } else static if(!is(Parameters!func[0] == T)) { 22 return false; 23 } else { 24 return true; 25 } 26 } 27 28 /// 29 bool canCheck(alias Matcher, ParameterType)() { 30 static if(!isCallable!Matcher) { 31 return false; 32 } else static if(Parameters!Matcher.length != 1) { 33 return false; 34 } else static if(!is(Parameters!Matcher[0] == ParameterType)) { 35 return false; 36 } else static if(!is(ReturnType!Matcher == bool)) { 37 return false; 38 } else { 39 return true; 40 } 41 } 42 43 /// 44 auto checkEither(alias check, This: Either!(Left, Right), Left, Right)(This either) { 45 static if(canCheck!(check, Left)) { 46 if(either.isLeft && check(either.left)) { 47 return true; 48 } 49 } 50 51 static if(canCheck!(check, Right)) { 52 if(either.isRight && check(either.right)) { 53 return true; 54 } 55 } 56 57 return false; 58 } 59 60 /// 61 auto callWith(MapFunction, This: Either!(Left, Right), Left, Right)(This either, MapFunction mapFunction) if(isCallable!MapFunction) { 62 static if(Parameters!MapFunction.length == 0) { 63 return mapFunction(); 64 } 65 66 static if(Parameters!MapFunction.length == 1 && isCallableWith!(MapFunction, Left)) { 67 if(either.isLeft) { 68 return mapFunction(either.left); 69 } else { 70 assert(0, "Got a right value. The mapFunction can't be called."); 71 } 72 } 73 74 static if(Parameters!MapFunction.length == 1 && isCallableWith!(MapFunction, Right)) { 75 if(either.isRight) { 76 return mapFunction(either.right); 77 } else { 78 assert(0, "Got a left value. The mapFunction can't be called."); 79 } 80 } 81 82 static if(Parameters!MapFunction.length > 1) { 83 static assert(false, "The map function must get none or 1 argument."); 84 } 85 } 86 87 /// 88 auto callIfCan(MapFunction, This: Either!(Left, Right), Left, Right)(This either, MapFunction mapFunction) if(isCallable!MapFunction) { 89 This result = either; 90 91 static if(Parameters!MapFunction.length == 0) { 92 static if(hasVoidReturn!MapFunction) { 93 mapFunction(); 94 } else { 95 result = mapFunction().bind!This; 96 } 97 } 98 99 static if(Parameters!MapFunction.length == 1 && isCallableWith!(MapFunction, Left)) { 100 if(either.isLeft) { 101 static if(hasVoidReturn!MapFunction) { 102 mapFunction(either.left); 103 } else { 104 result = mapFunction(either.left).bind!This; 105 } 106 } 107 } 108 109 static if(Parameters!MapFunction.length == 1 && isCallableWith!(MapFunction, Right)) { 110 if(either.isRight) { 111 static if(hasVoidReturn!MapFunction) { 112 mapFunction(either.right); 113 } else { 114 result = mapFunction(either.right).bind!This; 115 } 116 } 117 } 118 119 static if(Parameters!MapFunction.length > 1) { 120 static assert(false, "The map function must get none or 1 argument."); 121 } 122 123 return result; 124 } 125 126 /// Returns true if the given struct can be use as an Either struct 127 template isEitherStruct(T) if(isAggregateType!T && hasMember!(T, "left") && hasMember!(T, "right")) { 128 enum isEitherStruct = true; 129 } 130 131 /// ditto 132 template isEitherStruct(T) if(isBuiltinType!T || !isAggregateType!(T) || !hasMember!(T, "left") || !hasMember!(T, "right")) { 133 enum isEitherStruct = false; 134 } 135 136 /// Returns true if the function returns void 137 template hasVoidReturn(Func) { 138 static if (is(ReturnType!Func == void)) { 139 enum hasVoidReturn = true; 140 } else { 141 enum hasVoidReturn = false; 142 } 143 } 144 145 /// Check if the matcher returns any of the provided return types 146 bool returnsAnyOf(Matcher, ReturnTypes...)() { 147 static if(ReturnTypes.length == 0) { 148 return false; 149 } else static if(is(ReturnType!Matcher == ReturnTypes[0])) { 150 return true; 151 } else { 152 return returnsAnyOf!(Matcher, ReturnTypes[1..$]); 153 } 154 } 155 156 /// 157 template EitherTypeOf(T, Left, Right) { 158 static if(isEitherStruct!T) { 159 alias EitherTypeOf = T; 160 } else static if(isCallable!T) { 161 alias EitherTypeOf = EitherTypeOf!(ReturnType!T, Left, Right); 162 } else static if(is(T == Left)) { 163 alias EitherTypeOf = Either!(T, Any); 164 } else { 165 alias EitherTypeOf = Either!(Any, T); 166 } 167 } 168 169 template PickDefinedType(A, B) { 170 static if(is(A == B) || is(B == Any)) { 171 alias PickDefinedType = A; 172 } else static if(is(A == Any)) { 173 alias PickDefinedType = B; 174 } else { 175 static assert(false, A.stringof ~ " is not compatible with " ~ B.stringof); 176 } 177 } 178 179 /// 180 template NewLeft(This: Either!(Left, Right), That, Left, Right) { 181 alias EitherThisLeft = Left; 182 alias EitherThatLeft = typeof(EitherTypeOf!(That, Left, Right).left); 183 184 alias NewLeft = PickDefinedType!(EitherThisLeft, EitherThatLeft); 185 } 186 187 /// 188 template NewRight(This: Either!(Left, Right), That, Left, Right) { 189 alias EitherThisRight = Right; 190 alias EitherThatRight = typeof(EitherTypeOf!(That, Left, Right).right); 191 192 alias NewRight = PickDefinedType!(EitherThisRight, EitherThatRight); 193 } 194 195 /// 196 private auto resolve(T)(T newValue) { 197 static if(isCallable!newValue) { 198 return newValue(); 199 } else { 200 return newValue; 201 } 202 } 203 204 /// 205 struct Any { } 206 207 /// The Either type represents values with two possibilities: 208 /// a value of type Either a b is either Left a or Right b. 209 /// 210 /// The Either type is sometimes used to represent a value which is either correct or an error; 211 /// by convention, the Left constructor is used to hold an error value and the Right constructor 212 /// is used to hold a correct value (mnemonic: "right" also means "correct"). 213 struct Either(Left, Right) if(!is(Left == Right)) { 214 /// The type of this Either struct 215 alias This = Either!(Left, Right); 216 217 private { 218 Left left; 219 Right right; 220 EitherSide side; 221 } 222 223 /// Initialise the struct with the Left type 224 this(Left value) { 225 left = value; 226 side = EitherSide.Left; 227 } 228 229 /// Initialise the struct with the Right type 230 this(Right value) { 231 right = value; 232 side = EitherSide.Right; 233 } 234 } 235 236 /// returns true when the Left type is stored 237 bool isLeft(T : Either!(Left, Right), Left, Right)(T either) { 238 return either.side == EitherSide.Left; 239 } 240 241 /// isLeft is true when the struct is setup with the left type 242 unittest { 243 auto either = Either!(int, bool)(1); 244 245 either.isLeft.should.equal(true); 246 } 247 248 /// isLeft is false when the struct is setup with the right type 249 unittest { 250 auto either = Either!(int, bool)(true); 251 252 either.isLeft.should.equal(false); 253 } 254 255 /// returns true when the Right type is stored 256 bool isRight(T : Either!(Left, Right), Left, Right)(T either) { 257 return either.side == EitherSide.Right; 258 } 259 260 /// isRight is false when the struct is setup with the left type 261 unittest { 262 auto either = Either!(int, bool)(1); 263 264 either.isRight.should.equal(false); 265 } 266 267 /// isRight is true when the struct is setup with the right type 268 unittest { 269 auto either = Either!(int, bool)(true); 270 271 either.isRight.should.equal(true); 272 } 273 274 version(unittest) { 275 alias TestEither = Either!(int, Exception); 276 } 277 278 version(unittest) { 279 bool alwaysTrue(bool) { 280 return true; 281 } 282 283 bool alwaysTrueInt(int) { 284 return true; 285 } 286 287 bool alwaysFalse(bool) { 288 return false; 289 } 290 291 bool alwaysFalseInt(int) { 292 return false; 293 } 294 } 295 296 /// Wraps a value as an Either monad 297 Either!(Any, T) bind(T)(T value) if(!isEitherStruct!T) { 298 return Either!(Any, T)(value); 299 } 300 301 /// ditto 302 Either!(Left, Right) bind(Left, Right, T)(T value) if(!isEitherStruct!Left && !isEitherStruct!Right && !isEitherStruct!T) { 303 return Either!(Left, Right)(value); 304 } 305 306 /// ditto 307 auto bind(E, T)(T value) if(isEitherStruct!E && !isEitherStruct!T) { 308 return E(value); 309 } 310 311 /// ditto 312 auto bind(T)(T value) if(isEitherStruct!T) { 313 return value; 314 } 315 316 /// ditto 317 void bind(T)() if(isEitherStruct!T) { 318 static assert(false, "You can't bind void."); 319 } 320 321 /// ditto 322 Either!(PickDefinedType!(L, Left), PickDefinedType!(R, Right)) 323 bind(Left, Right, This: Either!(L, R), L, R)(This value) 324 if(!isEitherStruct!Left && !isEitherStruct!Right && isEitherStruct!This) { 325 alias NewL = PickDefinedType!(L, Left); 326 alias NewR = PickDefinedType!(R, Right); 327 328 alias LocalEither = Either!(NewL, NewR); 329 330 static if(!is(NewL == Any) && !is(L == Any)) { 331 if(value.isLeft) { 332 return LocalEither(value.left); 333 } 334 } 335 336 static if(!is(NewR == Any) && !is(R == Any)) { 337 if(value.isRight) { 338 return LocalEither(value.right); 339 } 340 } 341 342 assert(false, "Can't bind value `" ~ value.to!string ~ "` of type `" ~ This.stringof ~ "` to `" ~ LocalEither.stringof ~ "` ."); 343 } 344 345 /// returns a right hand Either when the value is an int 346 unittest { 347 auto result = bind(5); 348 349 result.isLeft.should.equal(false); 350 result.isRight.should.equal(true); 351 } 352 353 // Type matchers 354 355 /// Match Left or Right values using types 356 This when(Matcher, This: Either!(Left, Right), Left, Right)(This either, Matcher matcher) if(isCallable!Matcher) { 357 return either.callIfCan(matcher); 358 } 359 360 /// 'when' is called with the left value function when the monad isLeft is true 361 unittest { 362 auto either = Either!(int, bool)(1); 363 string result = "none"; 364 365 either 366 .when((int value) { 367 result = "left"; 368 }) 369 .when((bool value) { 370 result = "right"; 371 }); 372 373 result.should.equal("left"); 374 } 375 376 /// it calls the 'when' with right value function when the monad isRight is true 377 unittest { 378 auto either = Either!(int, bool)(true); 379 string result = "none"; 380 381 either 382 .when((int value) { 383 result = "left"; 384 }) 385 .when((bool value) { 386 result = "right"; 387 }); 388 389 result.should.equal("right"); 390 } 391 392 /// it does not call the 'when' function when the types don't match 393 unittest { 394 auto either = Either!(int, bool)(true); 395 string result = "none"; 396 397 either 398 .when((double value) { 399 result = "double"; 400 }) 401 .when((string value) { 402 result = "string"; 403 }); 404 405 result.should.equal("none"); 406 } 407 408 /// it returns the binded value when the 'when' function returns 409 unittest { 410 auto either = Either!(int, bool)(1); 411 bool message; 412 413 auto result = either 414 .when((int value) { 415 return true; 416 }) 417 .when((bool value) { 418 message = value; 419 }); 420 421 result.isRight.should.equal(true); 422 message.should.equal(true); 423 } 424 425 426 // Value matchers 427 428 /// Match Left or Right values by value examples 429 auto when(alias value, T, This: Either!(Left, Right), Left, Right)(This either, T newValue) if(!isCallable!value) { 430 auto result = newValue.resolve; 431 alias ResultType = typeof(result); 432 433 alias NewL = NewLeft!(This, ResultType); 434 alias NewR = NewRight!(This, ResultType); 435 alias ValueType = typeof(value); 436 437 static if(is(ValueType == Right)) { 438 if(either.isRight && value == either.right) { 439 return result.bind!(NewL, NewR); 440 } 441 442 return either.right.bind!(NewL, NewR); 443 } 444 445 static if(is(ValueType == Left)) { 446 if(either.isLeft && value == either.left) { 447 return result.bind!(NewL, NewR); 448 } 449 450 return either.left.bind!(NewL, NewR); 451 } 452 } 453 454 /// it is called when the Either value matches the when!Left value 455 unittest { 456 auto either = Either!(int, bool)(1); 457 bool message; 458 459 auto result = either 460 .when!1 ({ 461 return true; 462 }) 463 .when((bool value) { 464 message = value; 465 }); 466 467 result.isRight.should.equal(true); 468 message.should.equal(true); 469 } 470 471 /// it is not called when the value matches the Left 472 unittest { 473 auto either = Either!(int, bool)(1); 474 bool message; 475 476 auto result = either 477 .when!11 ({ 478 return true; 479 }) 480 .when((bool value) { 481 message = value; 482 }); 483 484 result.isLeft.should.equal(true); 485 message.should.equal(false); 486 } 487 488 /// it is called when the value matches the Right 489 unittest { 490 auto either = Either!(int, bool)(true); 491 int message; 492 493 auto result = either 494 .when!true ({ 495 return 2; 496 }) 497 .when((int value) { 498 message = value; 499 }); 500 501 result.isLeft.should.equal(true); 502 message.should.equal(2); 503 } 504 505 /// it does not call the 'when' function when the value matches the Left 506 unittest { 507 auto either = Either!(int, bool)(true); 508 bool message; 509 510 auto result = either 511 .when!false ({ 512 return 3; 513 }) 514 .when((bool value) { 515 message = value; 516 }); 517 518 result.isRight.should.equal(true); 519 message.should.equal(true); 520 } 521 522 /// it returns the 'when' value when the value matches the Right 523 unittest { 524 auto either = Either!(int, bool)(true); 525 int message; 526 527 auto result = either 528 .when!true (2) 529 .when((int value) { 530 message = value; 531 }); 532 533 result.isLeft.should.equal(true); 534 message.should.equal(2); 535 } 536 537 /// it does not return the 'when' value when the value is not matched 538 unittest { 539 auto either = Either!(int, bool)(2); 540 bool message; 541 542 auto result = either 543 .when!3 (true) 544 .when((bool value) { 545 message = value; 546 }); 547 548 result.isLeft.should.equal(true); 549 message.should.equal(false); 550 } 551 552 /// it does not return the 'when' value when the value is not matched 553 unittest { 554 auto either = Either!(int, bool)(true); 555 bool message; 556 557 auto result = either 558 .when!false (3) 559 .when((bool value) { 560 message = value; 561 }); 562 563 result.isRight.should.equal(true); 564 message.should.equal(true); 565 } 566 567 /// it returns the 'when' value when the value matches the Left 568 unittest { 569 auto either = Either!(int, bool)(1); 570 bool message; 571 572 auto result = either 573 .when!1 (true) 574 .when((bool value) { 575 message = value; 576 }); 577 578 result.isRight.should.equal(true); 579 message.should.equal(true); 580 } 581 582 /// it returns the new Either type 583 unittest { 584 auto either = 5.bind; 585 586 expect(typeof(either).stringof).to.equal("Either!(Any, int)"); 587 588 auto result = either.when!(5) ("The value is 5!".bind!(string, int)); 589 590 expect(typeof(result).stringof).to.equal("Either!(string, int)"); 591 expect(result.left).to.equal("The value is 5!"); 592 } 593 594 /// returning a left value with any 595 unittest { 596 auto result = 0.bind.when!(0) ("got zero!".bindLeft); 597 598 result.isLeft.should.equal(true); 599 result.left.should.equal("got zero!"); 600 } 601 602 /// Match Left or Right values using a check function 603 Either!(NewLeft!(This, T), NewRight!(This, T)) when(alias check, T, This: Either!(Left, Right), Left, Right)(This either, T result) if(isCallable!check) { 604 alias NewL = NewLeft!(This, T); 605 alias NewR = NewRight!(This, T); 606 607 if(either.checkEither!(check)) { 608 static if(isCallable!result) { 609 return either.callWith(result).bind!(NewL, NewR); 610 } else { 611 return result.bind!(NewL, NewR); 612 } 613 } 614 615 return either.bind!(NewL, NewR); 616 } 617 618 /// it calls the 'when' function when the function check returns true for Right value 619 unittest { 620 auto either = Either!(int, bool)(true); 621 int message; 622 623 auto result = either 624 .when!alwaysTrue ({ 625 return 2; 626 }) 627 .when((int value) { 628 message = value; 629 }); 630 631 result.isLeft.should.equal(true); 632 message.should.equal(2); 633 } 634 635 /// it calls the 'when' with the value function when the function check returns true for Right value 636 unittest { 637 auto either = Either!(int, bool)(true); 638 int message; 639 640 auto result = either 641 .when!alwaysTrue ((bool value) { 642 return 2; 643 }) 644 .when((int value) { 645 message = value; 646 }); 647 648 result.isLeft.should.equal(true); 649 message.should.equal(2); 650 } 651 652 /// it does not call the 'when' function when the function check returns false for Right value 653 unittest { 654 auto either = Either!(int, bool)(true); 655 bool message; 656 657 auto result = either 658 .when!alwaysFalse ({ 659 return 2; 660 }) 661 .when((bool value) { 662 message = value; 663 }); 664 665 result.isRight.should.equal(true); 666 message.should.equal(true); 667 } 668 669 /// it calls the 'when' function when the function check returns true for Left value 670 unittest { 671 auto either = Either!(int, bool)(8); 672 int message; 673 674 auto result = either 675 .when!alwaysTrueInt ({ 676 return 2; 677 }) 678 .when((int value) { 679 message = value; 680 }); 681 682 result.isLeft.should.equal(true); 683 message.should.equal(2); 684 } 685 686 /// 687 auto bindLeft(T)(T value) { 688 return Either!(T, Any)(value); 689 }