1 module either; 2 3 import std.traits; 4 import std.conv; 5 6 version(unittest) { 7 import _expect; 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 right = Right.init; 227 side = EitherSide.Left; 228 } 229 230 /// Initialise the struct with the Right type 231 this(Right value) { 232 right = value; 233 left = Left.init; 234 side = EitherSide.Right; 235 } 236 } 237 238 /// returns true when the Left type is stored 239 bool isLeft(T : Either!(Left, Right), Left, Right)(T either) { 240 return either.side == EitherSide.Left; 241 } 242 243 /// isLeft is true when the struct is setup with the left type 244 unittest { 245 auto either = Either!(int, bool)(1); 246 247 either.isLeft.should.equal(true); 248 } 249 250 /// isLeft is false when the struct is setup with the right type 251 unittest { 252 auto either = Either!(int, bool)(true); 253 254 either.isLeft.should.equal(false); 255 } 256 257 /// returns true when the Right type is stored 258 bool isRight(T : Either!(Left, Right), Left, Right)(T either) { 259 return either.side == EitherSide.Right; 260 } 261 262 /// isRight is false when the struct is setup with the left type 263 unittest { 264 auto either = Either!(int, bool)(1); 265 266 either.isRight.should.equal(false); 267 } 268 269 /// isRight is true when the struct is setup with the right type 270 unittest { 271 auto either = Either!(int, bool)(true); 272 273 either.isRight.should.equal(true); 274 } 275 276 version(unittest) { 277 private alias TestEither = Either!(int, Exception); 278 } 279 280 version(unittest) { 281 private bool alwaysTrue(bool) { 282 return true; 283 } 284 285 private bool alwaysTrueInt(int) { 286 return true; 287 } 288 289 private bool alwaysFalse(bool) { 290 return false; 291 } 292 293 private bool alwaysFalseInt(int) { 294 return false; 295 } 296 } 297 298 /// Wraps a value as an Either monad 299 Either!(Any, T) bind(T)(T value) if(!isEitherStruct!T) { 300 return Either!(Any, T)(value); 301 } 302 303 /// ditto 304 Either!(Left, Right) bind(Left, Right, T)(T value) if(!isEitherStruct!Left && !isEitherStruct!Right && !isEitherStruct!T) { 305 return Either!(Left, Right)(value); 306 } 307 308 /// ditto 309 auto bind(E, T)(T value) if(isEitherStruct!E && !isEitherStruct!T) { 310 return E(value); 311 } 312 313 /// ditto 314 auto bind(T)(T value) if(isEitherStruct!T) { 315 return value; 316 } 317 318 /// ditto 319 void bind(T)() if(isEitherStruct!T) { 320 static assert(false, "You can't bind void."); 321 } 322 323 /// ditto 324 Either!(PickDefinedType!(L, Left), PickDefinedType!(R, Right)) 325 bind(Left, Right, This: Either!(L, R), L, R)(This value) 326 if(!isEitherStruct!Left && !isEitherStruct!Right && isEitherStruct!This) { 327 alias NewL = PickDefinedType!(L, Left); 328 alias NewR = PickDefinedType!(R, Right); 329 330 alias LocalEither = Either!(NewL, NewR); 331 332 static if(!is(NewL == Any) && !is(L == Any)) { 333 if(value.isLeft) { 334 return LocalEither(value.left); 335 } 336 } 337 338 static if(!is(NewR == Any) && !is(R == Any)) { 339 if(value.isRight) { 340 return LocalEither(value.right); 341 } 342 } 343 344 assert(false, "Can't bind value `" ~ value.to!string ~ "` of type `" ~ This.stringof ~ "` to `" ~ LocalEither.stringof ~ "` ."); 345 } 346 347 /// returns a right hand Either when the value is an int 348 unittest { 349 auto result = bind(5); 350 351 result.isLeft.should.equal(false); 352 result.isRight.should.equal(true); 353 } 354 355 // Type matchers 356 357 /// Match Left or Right values using types 358 This when(Matcher, This: Either!(Left, Right), Left, Right)(This either, Matcher matcher) if(isCallable!Matcher) { 359 return either.callIfCan(matcher); 360 } 361 362 /// 'when' is called with the left value function when the monad isLeft is true 363 unittest { 364 auto either = Either!(int, bool)(1); 365 string result = "none"; 366 367 either 368 .when((int value) { 369 result = "left"; 370 }) 371 .when((bool value) { 372 result = "right"; 373 }); 374 375 result.should.equal("left"); 376 } 377 378 /// it calls the 'when' with right value function when the monad isRight is true 379 unittest { 380 auto either = Either!(int, bool)(true); 381 string result = "none"; 382 383 either 384 .when((int value) { 385 result = "left"; 386 }) 387 .when((bool value) { 388 result = "right"; 389 }); 390 391 result.should.equal("right"); 392 } 393 394 /// it does not call the 'when' function when the types don't match 395 unittest { 396 auto either = Either!(int, bool)(true); 397 string result = "none"; 398 399 either 400 .when((double value) { 401 result = "double"; 402 }) 403 .when((string value) { 404 result = "string"; 405 }); 406 407 result.should.equal("none"); 408 } 409 410 /// it returns the binded value when the 'when' function returns 411 unittest { 412 auto either = Either!(int, bool)(1); 413 bool message; 414 415 auto result = either 416 .when((int value) { 417 return true; 418 }) 419 .when((bool value) { 420 message = value; 421 }); 422 423 result.isRight.should.equal(true); 424 message.should.equal(true); 425 } 426 427 428 // Value matchers 429 430 /// Match Left or Right values by value examples 431 auto when(alias value, T, This: Either!(Left, Right), Left, Right)(This either, T newValue) if(!isCallable!value) { 432 auto result = newValue.resolve; 433 alias ResultType = typeof(result); 434 435 alias NewL = NewLeft!(This, ResultType); 436 alias NewR = NewRight!(This, ResultType); 437 alias ValueType = typeof(value); 438 439 static if(is(ValueType == Right)) { 440 if(either.isRight && value == either.right) { 441 return result.bind!(NewL, NewR); 442 } 443 444 return either.right.bind!(NewL, NewR); 445 } 446 447 static if(is(ValueType == Left)) { 448 if(either.isLeft && value == either.left) { 449 return result.bind!(NewL, NewR); 450 } 451 452 return either.left.bind!(NewL, NewR); 453 } 454 } 455 456 /// it is called when the Either value matches the when!Left value 457 unittest { 458 auto either = Either!(int, bool)(1); 459 bool message; 460 461 auto result = either 462 .when!1 ({ 463 return true; 464 }) 465 .when((bool value) { 466 message = value; 467 }); 468 469 result.isRight.should.equal(true); 470 message.should.equal(true); 471 } 472 473 /// it is not called when the value matches the Left 474 unittest { 475 auto either = Either!(int, bool)(1); 476 bool message; 477 478 auto result = either 479 .when!11 ({ 480 return true; 481 }) 482 .when((bool value) { 483 message = value; 484 }); 485 486 result.isLeft.should.equal(true); 487 message.should.equal(false); 488 } 489 490 /// it is called when the value matches the Right 491 unittest { 492 auto either = Either!(int, bool)(true); 493 int message; 494 495 auto result = either 496 .when!true ({ 497 return 2; 498 }) 499 .when((int value) { 500 message = value; 501 }); 502 503 result.isLeft.should.equal(true); 504 message.should.equal(2); 505 } 506 507 /// it does not call the 'when' function when the value matches the Left 508 unittest { 509 auto either = Either!(int, bool)(true); 510 bool message; 511 512 auto result = either 513 .when!false ({ 514 return 3; 515 }) 516 .when((bool value) { 517 message = value; 518 }); 519 520 result.isRight.should.equal(true); 521 message.should.equal(true); 522 } 523 524 /// it returns the 'when' value when the value matches the Right 525 unittest { 526 auto either = Either!(int, bool)(true); 527 int message; 528 529 auto result = either 530 .when!true (2) 531 .when((int value) { 532 message = value; 533 }); 534 535 result.isLeft.should.equal(true); 536 message.should.equal(2); 537 } 538 539 /// it does not return the 'when' value when the value is not matched 540 unittest { 541 auto either = Either!(int, bool)(2); 542 bool message; 543 544 auto result = either 545 .when!3 (true) 546 .when((bool value) { 547 message = value; 548 }); 549 550 result.isLeft.should.equal(true); 551 message.should.equal(false); 552 } 553 554 /// it does not return the 'when' value when the value is not matched 555 unittest { 556 auto either = Either!(int, bool)(true); 557 bool message; 558 559 auto result = either 560 .when!false (3) 561 .when((bool value) { 562 message = value; 563 }); 564 565 result.isRight.should.equal(true); 566 message.should.equal(true); 567 } 568 569 /// it returns the 'when' value when the value matches the Left 570 unittest { 571 auto either = Either!(int, bool)(1); 572 bool message; 573 574 auto result = either 575 .when!1 (true) 576 .when((bool value) { 577 message = value; 578 }); 579 580 result.isRight.should.equal(true); 581 message.should.equal(true); 582 } 583 584 /// it returns the new Either type 585 unittest { 586 auto either = 5.bind; 587 588 expect(typeof(either).stringof).to.equal("Either!(Any, int)"); 589 590 auto result = either.when!(5) ("The value is 5!".bind!(string, int)); 591 592 expect(typeof(result).stringof).to.equal("Either!(string, int)"); 593 expect(result.left).to.equal("The value is 5!"); 594 } 595 596 /// returning a left value with any 597 unittest { 598 auto result = 0.bind.when!(0) ("got zero!".bindLeft); 599 600 result.isLeft.should.equal(true); 601 result.left.should.equal("got zero!"); 602 } 603 604 /// Match Left or Right values using a check function 605 Either!(NewLeft!(This, T), NewRight!(This, T)) when(alias check, T, This: Either!(Left, Right), Left, Right)(This either, T result) if(isCallable!check) { 606 alias NewL = NewLeft!(This, T); 607 alias NewR = NewRight!(This, T); 608 609 if(either.checkEither!(check)) { 610 static if(isCallable!result) { 611 return either.callWith(result).bind!(NewL, NewR); 612 } else { 613 return result.bind!(NewL, NewR); 614 } 615 } 616 617 return either.bind!(NewL, NewR); 618 } 619 620 /// Bind nested structs 621 unittest { 622 // Nested structs need to be explicitly initialized in the constructor. 623 import std.algorithm.iteration; 624 auto base = [0,1,2]; 625 auto l = bindLeft(base.map!( a => a + 1)); 626 auto r = bind(base.map!( a => a + 1)); 627 } 628 629 630 /// it calls the 'when' function when the function check returns true for Right value 631 unittest { 632 auto either = Either!(int, bool)(true); 633 int message; 634 635 auto result = either 636 .when!alwaysTrue ({ 637 return 2; 638 }) 639 .when((int value) { 640 message = value; 641 }); 642 643 result.isLeft.should.equal(true); 644 message.should.equal(2); 645 } 646 647 /// it calls the 'when' with the value function when the function check returns true for Right value 648 unittest { 649 auto either = Either!(int, bool)(true); 650 int message; 651 652 auto result = either 653 .when!alwaysTrue ((bool value) { 654 return 2; 655 }) 656 .when((int value) { 657 message = value; 658 }); 659 660 result.isLeft.should.equal(true); 661 message.should.equal(2); 662 } 663 664 /// it does not call the 'when' function when the function check returns false for Right value 665 unittest { 666 auto either = Either!(int, bool)(true); 667 bool message; 668 669 auto result = either 670 .when!alwaysFalse ({ 671 return 2; 672 }) 673 .when((bool value) { 674 message = value; 675 }); 676 677 result.isRight.should.equal(true); 678 message.should.equal(true); 679 } 680 681 /// it calls the 'when' function when the function check returns true for Left value 682 unittest { 683 auto either = Either!(int, bool)(8); 684 int message; 685 686 auto result = either 687 .when!alwaysTrueInt ({ 688 return 2; 689 }) 690 .when((int value) { 691 message = value; 692 }); 693 694 result.isLeft.should.equal(true); 695 message.should.equal(2); 696 } 697 698 /// 699 auto bindLeft(T)(T value) { 700 return Either!(T, Any)(value); 701 }