1 /** This module implements a variety of classes derived from Function. */ 2 module generald.functions; 3 4 import std.typecons, std.traits; 5 6 alias Maybe = Nullable; 7 8 /// A class representing a function. 9 abstract class Function(A, B) 10 { 11 abstract B opCall(A); 12 alias InputType = A; 13 alias OutputType = B; 14 } 15 16 /// Template for converting a function to Function instance. 17 class RealFunction(alias f, A=ParameterTypeTuple!f[0], B=ReturnType!f) : Function!(A, B) 18 { 19 pragma(msg, "RealFunction(", A.stringof, " -> ", ParameterTypeTuple!f[0].stringof, " -> ", ReturnType!f.stringof, " -> ", B.stringof, ")"); 20 override B opCall(A x) 21 { 22 return f(x); 23 } 24 mixin Singleton; 25 } 26 27 /// 28 unittest 29 { 30 static int f(long x) 31 { 32 return cast(int)((x >> 32) ^ (x & ((1UL << 32) - 1))); 33 } 34 static assert (is (RealFunction!f : Function!(long, int))); 35 static assert (is (RealFunction!(f, int, int) : Function!(int, int))); 36 static assert (is (RealFunction!(f, int, long) : Function!(int, long))); 37 static assert (is (RealFunction!(f, long, long) : Function!(long, long))); 38 } 39 40 /// Identity function. 41 T id(T)(T x) 42 { 43 return x; 44 } 45 46 /// ditto 47 alias IdentityFunction(T) = RealFunction!(id!T); 48 49 /// 50 unittest 51 { 52 auto f = IdentityFunction!int.get; 53 assert (f(0) == 0); 54 } 55 56 /// Compose two Functions. 57 class ComposedFunction(A, B, C, D) : Function!(A, D) 58 if (is (B : C)) 59 { 60 Function!(A, B) f; 61 Function!(C, D) g; 62 this (Function!(A, B) f, Function!(C, D) g) 63 { 64 this.f = f; 65 this.g = g; 66 } 67 override D opCall(A x) 68 { 69 return g(f(x)); 70 } 71 } 72 73 /// ditto 74 auto compose(F, G)(F f, G g) 75 if (is (F : Function!(F.InputType, F.OutputType)) && 76 is (G : Function!(G.InputType, G.OutputType)) && 77 is (F.OutputType : G.InputType)) 78 { 79 return new ComposedFunction!(F.InputType, F.OutputType, G.InputType, G.OutputType)(f, g); 80 } 81 82 /// 83 unittest 84 { 85 auto f = RealFunction!(triple!int).get.compose(RealFunction!(increment!int).get); 86 auto g = RealFunction!(increment!int).get.compose(RealFunction!(triple!int).get); 87 assert (f(0) == 1); 88 assert (f(1) == 4); 89 assert (g(0) == 3); 90 assert (g(1) == 6); 91 } 92 93 /// Curry a function. 94 class CurriedFunction(A, B, C) : Function!(A, Function!(B, C)) 95 { 96 Function!(Tuple!(A, B), C) uncurried; 97 this (Function!(Tuple!(A, B), C) uncurried) 98 { 99 this.uncurried = uncurried; 100 } 101 class Partial : Function!(B, C) 102 { 103 A x; 104 this (A x) 105 { 106 this.x = x; 107 } 108 override C opCall(B x) 109 { 110 return uncurried(this.x.tuple(x)); 111 } 112 } 113 override Function!(B, C) opCall(A x) 114 { 115 return new Partial(x); 116 } 117 } 118 119 /// ditto 120 auto curry(F)(F f) 121 { 122 static if (is (F.InputType : Tuple!(A, B), A, B)) 123 return new CurriedFunction!(A, B, F.OutputType)(f); 124 else static assert (false); 125 } 126 127 /// 128 unittest 129 { 130 static class F : Function!(Tuple!(int, int), int) 131 { 132 override int opCall(Tuple!(int, int) x) 133 { 134 return x[0] + x[1]; 135 } 136 mixin Singleton; 137 } 138 auto cf = F.get.curry; 139 static assert (is (typeof (cf) : Function!(int, Function!(int, int)))); 140 } 141 142 /// Uncurry a function. 143 class UncurriedFunction(A, B, C) : Function!(Tuple!(A, B), C) 144 { 145 Function!(A, Function!(B, C)) curried; 146 this (Function!(A, Function!(B, C)) curried) 147 { 148 this.curried = curried; 149 } 150 override C opCall(Tuple!(A, B) x) 151 { 152 return curried(x[0])(x[1]); 153 } 154 } 155 156 /// ditto 157 auto uncurry(F)(F f) 158 { 159 static if (is (F.OutputType == Function!(B, C), B, C)) 160 return new UncurriedFunction!(F.InputType, B, C)(f); 161 else static assert (false); 162 } 163 164 /// 165 unittest 166 { 167 static class G : Function!(int, Function!(int, int)) 168 { 169 170 class g : Function!(int, int) 171 { 172 int x; 173 this (int x) 174 { 175 this.x = x; 176 } 177 override int opCall(int x) 178 { 179 return this.x + x; 180 } 181 } 182 override Function!(int, int) opCall(int x) 183 { 184 return new g(x); 185 } 186 mixin Singleton; 187 } 188 auto ug = G.get.uncurry; 189 static assert (is (typeof (ug) : Function!(Tuple!(int, int), int))); 190 } 191 192 /// Function which always return Null. 193 Maybe!A nothing(A)() 194 { 195 return Maybe!A(); 196 } 197 198 /// Return function for Maybe. 199 Maybe!A just(A)(A x) 200 { 201 return Maybe!A(x); 202 } 203 /// ditto 204 alias maybeReturn(A) = just!A; 205 /// ditto 206 alias MaybeReturn(A) = RealFunction!(maybeReturn!A); 207 208 /// Bind function for Maybe: (a -> Maybe b) -> (Maybe a -> Maybe b). 209 class MaybeBind(A, B) : Function!(Maybe!A, Maybe!B) 210 { 211 Function!(A, Maybe!B) f; 212 this (Function!(A, Maybe!B) f) 213 { 214 this.f = f; 215 } 216 override Maybe!B opCall(Maybe!A x) 217 { 218 if (x.isNull) 219 return nothing!B; 220 return f(x.get); 221 } 222 } 223 224 /// ditto 225 auto maybeBind(F)(F f) 226 { 227 static if (is (F.OutputType : Maybe!B, B)) 228 return new MaybeBind!(F.InputType, B)(f); 229 else static assert (false); 230 } 231 232 /// Map function for Maybe. 233 auto maybeMap(F)(F f) 234 if (is (F : Function!(A, B), A, B)) 235 { 236 return f.compose(MaybeReturn!(F.OutputType).get).maybeBind; 237 } 238 239 /// 240 unittest 241 { 242 auto maybeId = maybeMap(RealFunction!(id!int).get); 243 assert (maybeId(just(0)) == just(0)); 244 } 245 246 /// Compose two Functions f and g where f emits a Maybe!B and g takes a B. 247 auto maybeCompose(F, G)(F f, G g) 248 { 249 return f.compose(g.maybeBind); 250 } 251 252 /// 253 unittest 254 { 255 auto c = RealFunction!(collatz!int).get; 256 auto lc = c.maybeCompose(c).maybeCompose(c); 257 auto rc = c.maybeCompose(c.maybeCompose(c)); 258 foreach (i; 0..10) 259 assert (lc(i).maybeEqual(rc(i))); 260 } 261 262 /// Function from Maybe to void can be constructed from a Function to void. 263 class MaybeSink(A) : Function!(Maybe!A, void) 264 { 265 Function!(A, void) sink; 266 this (Function!(A, void) sink) 267 { 268 this.sink = sink; 269 } 270 override void opCall(Maybe!A x) 271 { 272 if (x.isNull) 273 return; 274 sink(x); 275 } 276 } 277 278 /// ditto 279 auto maybeSink(S)(S sink) 280 if (is (S : Function!(A, void), A)) 281 { 282 return new MaybeSink!(S.InputType)(sink); 283 } 284 285 /// Maybe do something, and return null. 286 class MaybeNothing(A, B, C=void) : Function!(A, Maybe!B) 287 { 288 Function!(A, C) sink; 289 this (Function!(A, C) sink) 290 { 291 this.sink = sink; 292 } 293 override Maybe!B opCall(A x) 294 { 295 sink(x); 296 return nothing!B; 297 } 298 } 299 300 /// ditto 301 auto maybeNothing(B, F)(F f) 302 { 303 return new MaybeNothing!(F.InputType, B, F.OutputType)(f); 304 } 305 306 307 /// Either type. 308 struct Either(A, B) 309 if (!is (A : B) && !is (B : A)) 310 { 311 this (A a) 312 { 313 this.nonNull = true; 314 this.which = false; 315 this._a = a; 316 } 317 this (B b) 318 { 319 this.nonNull = true; 320 this.which = true; 321 this._b = b; 322 } 323 typeof (this) opAssign(A a) 324 { 325 this.nonNull = true; 326 this.which = false; 327 this._a = a; 328 return this; 329 } 330 typeof (this) opAssign(B b) 331 { 332 this.nonNull = true; 333 this.which = true; 334 this._b = b; 335 return this; 336 } 337 string toString() 338 { 339 import std.conv; 340 if (!which) 341 return _a.to!string; 342 else 343 return _b.to!string; 344 } 345 alias Left = A; 346 alias Right = B; 347 private: 348 bool nonNull, which; 349 A _a; 350 B _b; 351 @property A a() 352 in 353 { 354 assert (this.nonNull); 355 assert (!this.which); 356 } 357 body 358 { 359 return _a; 360 } 361 @property B b() 362 in 363 { 364 assert (this.nonNull); 365 assert ( this.which); 366 } 367 body 368 { 369 return _b; 370 } 371 } 372 373 alias Left(A) = Typedef!(A, A.init, "left"); 374 alias Right(A) = Typedef!(A, A.init, "right"); 375 alias Either(A) = Either!(Left!A, Right!A); 376 377 /// Tuple of functions, which takes an Either. 378 class EitherFunction(A, B, C) : Function!(Either!(A, B), C) 379 { 380 Function!(A, C) f; 381 Function!(B, C) g; 382 this (Function!(A, C) f, Function!(B, C) g) 383 { 384 this.f = f; 385 this.g = g; 386 } 387 override C opCall(Either!(A, B) x) 388 { 389 if (!x.which) 390 return f(x.a); 391 else 392 return g(x.b); 393 } 394 } 395 396 /// ditto 397 auto eitherFunction(F, G)(F f, G g) 398 if (is (F.OutputType == G.OutputType)) 399 { 400 return new EitherFunction!(F.InputType, G.InputType, F.OutputType)(f, g); 401 } 402 403 /// Function to Either. 404 alias LeftEither(A, B) = RealFunction!(left!(B, A)); 405 406 /// ditto 407 alias RightEither(A, B) = RealFunction!(right!(A, B)); 408 409 /// Either!(, B) functor at A. 410 Either!(A, B) left(B, A)(A a) 411 { 412 Either!(A, B) x; 413 x = a; 414 return x; 415 } 416 417 /// Either!(A, ) functor at B. 418 Either!(A, B) right(A, B)(B b) 419 { 420 Either!(A, B) x; 421 x = b; 422 return x; 423 } 424 425 Either!A left(A)(A a) 426 { 427 Either!A x; 428 x = Left!A(a); 429 return x; 430 } 431 432 Either!A right(A)(A a) 433 { 434 Either!A x; 435 x = Right!A(a); 436 return x; 437 } 438 439 unittest 440 { 441 auto a = 1.left; 442 auto b = 1.right; 443 auto c = int(1).left!string; 444 auto d = int(1).right!string; 445 } 446 447 448 /// Function from and to Either. 449 auto eitherEither(F, G)(F f, G g) 450 { 451 return eitherFunction( 452 f.compose(LeftEither!(F.OutputType, G.OutputType).get), 453 g.compose(RightEither!(F.OutputType, G.OutputType).get)); 454 } 455 456 /// Tuple of functions, which returns a Tuple. 457 class FunctionTuple(A, B, C) : Function!(A, Tuple!(B, C)) 458 { 459 Function!(A, B) f; 460 Function!(A, C) g; 461 this (Function!(A, B) f, Function!(A, C) g) 462 { 463 this.f = f; 464 this.g = g; 465 } 466 override Tuple!(B, C) opCall(A x) 467 { 468 return tuple(f(x), g(x)); 469 } 470 } 471 472 /// ditto 473 auto functionTuple(F, G)(F f, G g) 474 if (is (F.InputType == G.InputType)) 475 { 476 return new FunctionTuple!(F.InputType, F.OutputType, G.OutputType)(f, g); 477 } 478 479 /// Function from Tuple. 480 auto tupleLeft(T)(T x) 481 { 482 return x[0]; 483 } 484 485 /// ditto 486 auto tupleRight(T)(T x) 487 { 488 return x[1]; 489 } 490 491 /// ditto 492 alias TupleLeft(A, B) = RealFunction!(tupleLeft!(Tuple!(A, B))); 493 494 /// ditto 495 alias TupleRight(A, B) = RealFunction!(tupleRight!(Tuple!(A, B))); 496 497 /// Function from and to Tuple. 498 auto tupleTuple(F, G)(F f, G g) 499 { 500 return functionTuple( 501 TupleLeft!(F.InputType, G.InputType).get.compose(f), 502 TupleRight!(F.InputType, G.InputType).get.compose(g)); 503 } 504 505 /// Function from Tuple of Maybe to Maybe of Tuple. 506 auto maybeTuple(TM)(TM x) 507 { 508 if (x[0].isNull || x[1].isNull) 509 return nothing!(Tuple!(typeof (x[0].get), typeof (x[1].get))); 510 return just(x[0].get.tuple(x[1].get)); 511 } 512 513 /// ditto 514 alias MaybeTuple(A, B) = RealFunction!(maybeTuple!(Tuple!(Maybe!A, Maybe!B))); 515 516 /// Returns the function which swaps the components of the given tuple. 517 auto swapper(A, B)() 518 { 519 return TupleRight!(A, B).get.functionTuple(TupleLeft!(A, B).get); 520 } 521 522 /// Compose with swapper. 523 auto swapResult(F)(F f) 524 { 525 return f.compose(swapper!(F.OutputType.Types[0], F.OutputType.Types[1])()); 526 } 527 528 /// 529 unittest 530 { 531 auto x = new Maybe!int[4]; 532 auto y = new Maybe!int[4]; 533 auto z = new Maybe!(Tuple!(int, int))[4]; 534 x[0] = 2; x[1] = 3; 535 y[0] = 5, y[2] = 7; 536 z[0] = 5.tuple(2); 537 auto p = IdentityFunction!(Tuple!(Maybe!int, Maybe!int)).get 538 .swapResult.compose(MaybeTuple!(int, int).get); 539 foreach (i; 0..4) 540 assert (p(x[i].tuple(y[i])).maybeEqual(z[i])); 541 } 542 543 /// (a -> b) -> (a -> (b, a)) 544 auto functionTupleIdentity(F)(F f) 545 { 546 return f.functionTuple(IdentityFunction!(F.InputType).get); 547 } 548 549 /// (a -> b) -> ((a, c) -> (b, c)) 550 auto tupleTupleIdentity(B, F)(F f) 551 { 552 return f.tupleTuple(IdentityFunction!B.get); 553 } 554 555 /// (a -> b) -> (a|b -> b) 556 auto eitherFunctionIdentity(F)(F f) 557 { 558 return f.eitherFunction(IdentityFunction!(F.OutputType).get); 559 } 560 561 /// (a -> b) -> (a|c -> b|c) 562 auto eitherEitherIdentity(B, F)(F f) 563 { 564 return f.eitherEither(IdentityFunction!B.get); 565 } 566 567 /// 568 unittest 569 { 570 static class TestedFunction : Function!(int, string) 571 { 572 override string opCall(int x) 573 { 574 import std.math, std.conv; 575 return sqrt(real(1) + x * x).to!string; 576 } 577 mixin Singleton; 578 } 579 auto t = TestedFunction.get; 580 auto 581 t0 = t.functionTupleIdentity, 582 t1 = t.tupleTupleIdentity!(int[]), 583 t2 = t.eitherFunctionIdentity, 584 t3 = t.eitherEitherIdentity!(int[]); 585 static assert (is (typeof (t0) : Function!(int, Tuple!(string, int)))); 586 static assert (is (typeof (t1) : Function!(Tuple!(int, int[]), Tuple!(string, int[])))); 587 static assert (is (typeof (t2) : Function!(Either!(int, string), string))); 588 static assert (is (typeof (t3) : Function!(Either!(int, int[]), Either!(string, int[])))); 589 } 590 591 /// Either of functions, which returns an Either. 592 class FunctionEither(A, B, C) : Function!(A, Either!(B, C)) 593 { 594 Either!(Function!(A, B), Function!(A, C)) f; 595 this (Either!(Function!(A, B), Function!(A, C)) f) 596 { 597 this.f = f; 598 } 599 this (Function!(A, B) f) 600 { 601 this.f = f; 602 } 603 this (Function!(A, C) f) 604 { 605 this.f = f; 606 } 607 override Either!(B, C) opCall(A x) 608 { 609 if (!f.which) 610 return f.a()(x).left!C; 611 else 612 return f.b()(x).right!B; 613 } 614 } 615 616 /// ditto 617 auto functionEitherLeft(C, F)(F f) 618 { 619 static if (is (F : Function!(A, B), A, B)) 620 return new FunctionEither!(A, B, C)(f); 621 else static assert (false); 622 } 623 624 /// ditto 625 auto functionEitherRight(B, F)(F f) 626 { 627 static if (is (F : Function!(A, C), A, C)) 628 return new FunctionEither!(A, B, C)(f); 629 else static assert (false); 630 } 631 632 /// 633 unittest 634 { 635 static real lreal(int x) 636 { 637 import std.math; 638 return PI * x; 639 } 640 static string rstring(int x) 641 { 642 import std.conv; 643 return x.to!string; 644 } 645 auto fl = RealFunction!lreal.get.functionEitherLeft!string; 646 auto fr = RealFunction!rstring.get.functionEitherRight!real; 647 static assert (is (typeof (fl) == typeof (fr))); 648 } 649 650 /// Either of functions, which takes a Tuple. 651 class TupleFunction(A, B, C) : Function!(Tuple!(A, B), C) 652 { 653 Either!(Function!(A, C), Function!(B, C)) f; 654 this (Either!(Function!(A, C), Function!(B, C)) f) 655 { 656 this.f = f; 657 } 658 this (Function!(A, C) f) 659 { 660 this.f = f; 661 } 662 this (Function!(B, C) f) 663 { 664 this.f = f; 665 } 666 override C opCall(Tuple!(A, B) x) 667 { 668 if (!f.which) 669 return f.a()(x[0]); 670 else 671 return f.b()(x[1]); 672 } 673 } 674 675 /// ditto 676 auto leftTupleFunction(B, F)(F f) 677 { 678 return new TupleFunction!(F.InputType, B, F.OutputType)(f); 679 } 680 681 /// ditto 682 auto rightTupleFunction(A, F)(F f) 683 { 684 return new TupleFunction!(A, F.InputType, F.OutputType)(f); 685 } 686 687 /// 688 unittest 689 { 690 static string cts(char x) 691 { 692 import std.conv : to; 693 return x.to!string; 694 } 695 static string rts(real x) 696 { 697 import std.conv : to; 698 return x.to!string; 699 } 700 auto l = RealFunction!cts.get.leftTupleFunction!real; 701 auto r = RealFunction!rts.get.rightTupleFunction!char; 702 static assert (is (typeof (l) == typeof (r))); 703 } 704 705 /// Takues an array of functions and return a function to array. 706 class FunctionArray(A, B) : Function!(A, B[]) 707 { 708 Function!(A, B)[] fs; 709 this (Function!(A, B)[] fs) 710 { 711 this.fs = fs; 712 } 713 override B[] opCall(A x) 714 { 715 B[] ret; 716 foreach (f; fs) 717 ret ~= f(x); 718 return ret; 719 } 720 } 721 722 /// ditto 723 auto functionArray(F)(F[] fs) 724 { 725 return new FunctionArray!(F.InputType, F.OutputType)(fs); 726 } 727 728 /// Map function for Array. 729 class ArrayMap(A, B) : Function!(A[], B[]) 730 { 731 Function!(A, B) f; 732 this (Function!(A, B) f) 733 { 734 this.f = f; 735 } 736 override B[] opCall(A[] xs) 737 { 738 B[] ret; 739 foreach (x; xs) 740 ret ~= f(x); 741 return ret; 742 } 743 } 744 745 /// ditto 746 auto arrayMap(F)(F f) 747 { 748 return new ArrayMap!(F.InputType, F.OutputType)(f); 749 } 750 751 /// Bind function for Array. 752 class ArrayBind(A, B) : Function!(A[], B[]) 753 { 754 Function!(A, B[]) f; 755 this (Function!(A, B[]) f) 756 { 757 this.f = f; 758 } 759 override B[] opCall(A[] xs) 760 { 761 B[] ret; 762 foreach (x; xs) 763 ret ~= f(x); 764 return ret; 765 } 766 } 767 768 /// ditto 769 auto arrayBind(F)(F f) 770 { 771 return new ArrayBind!(F.InputType, ElementType!(F.OutputType)); 772 } 773 774 /// Return function for Array. 775 auto arrayOnly(A)(A x) 776 { 777 return [x]; 778 } 779 /// ditto 780 alias arrayReturn(A) = arrayOnly!A; 781 /// ditto 782 alias ArrayReturn(A) = RealFunction!(arrayReturn!A); 783 784 /// Function from Array to void can be constructed from a Function to void. 785 class ArraySink(A) : Function!(A[], void) 786 { 787 Function!(A, void) sink; 788 this (Function!(A, void) sink) 789 { 790 this.sink = sink; 791 } 792 override void opCall(A[] xs) 793 { 794 foreach (x; xs) 795 sink(x); 796 } 797 } 798 799 /// ditto 800 auto arraySink(S)(S sink) 801 if (is (S : Function!(A, void), A)) 802 { 803 return new ArraySink!(S.InputType)(sink); 804 } 805 806 /// 807 unittest 808 { 809 import std.stdio, std.range, std.array; 810 RealFunction!(writeln!int).get.arraySink()(10.iota.array); 811 } 812 813 /// Take an array of functions and return a function from and to array. 814 class ArrayArray(A, B) : Function!(A[], B[]) 815 { 816 Function!(A, B)[] fs; 817 this (Function!(A, B)[] fs) 818 { 819 this.fs = fs; 820 } 821 @property size_t length() 822 { 823 return fs.length; 824 } 825 override B[] opCall(A[] xs) 826 in 827 { 828 assert (length == xs.length); 829 } 830 body 831 { 832 import std.range : zip; 833 B[] ret; 834 foreach (fx; fs.zip(xs)) 835 ret ~= fx[0](fx[1]); 836 return ret; 837 } 838 } 839 840 /// ditto 841 auto arrayArray(FS)(FS fs) 842 { 843 import std.range : ElementType; 844 return new ArrayArray!(ElementType!FS.InputType, ElementType!FS.OutputType)(fs); 845 } 846 847 /// 848 unittest 849 { 850 static class Adder : Function!(int, int) 851 { 852 int added; 853 this (int added) 854 { 855 this.added = added; 856 } 857 override int opCall(int x) 858 { 859 return x + added; 860 } 861 } 862 import std.range : iota; 863 import std.array : array; 864 Function!(int, int)[] adders; 865 foreach_reverse (i; 0..5) 866 adders ~= new Adder(i + 1); 867 assert (adders.arrayArray()(5.iota.array) == [5, 5, 5, 5, 5]); 868 } 869 870 /// Singleton pattern. 871 mixin template Singleton(Flag!"hideConstructor" hideConstructor = Yes.hideConstructor) 872 { 873 static typeof (this) get() 874 { 875 static typeof (this) instance; 876 if (instance is null) 877 return instance = new typeof (this)(); 878 return instance; 879 } 880 static if (hideConstructor) 881 private this () 882 { 883 } 884 } 885 886 debug 887 { 888 class Printer(T) : Function!(T, void) 889 { 890 override void opCall(T x) 891 { 892 import std.stdio; 893 writeln(x); 894 } 895 mixin Singleton; 896 } 897 auto autoPrint(F)(F f) 898 { 899 return f.compose(Printer!(F.OutputType).get); 900 } 901 auto autoPrintJustOnly(F)(F f) 902 { 903 static if (is (F.OutputType : Maybe!B, B)) 904 return f.compose(Printer!B.get.maybeSink); 905 else static assert (false); 906 } 907 } 908 909 version (unittest) 910 { 911 T triple(T)(T x) 912 { 913 return x * 3; 914 } 915 T increment(T)(T x) 916 { 917 return x + 1; 918 } 919 Maybe!T collatz(T)(T x) 920 { 921 if (x <= 1) 922 return nothing!T; 923 if (x & 1) 924 return just(x.triple.increment); 925 return just(x / 2); 926 } 927 bool maybeEqual(T)(Maybe!T x, Maybe!T y) 928 { 929 if (x.isNull || y.isNull) 930 return x.isNull && y.isNull; 931 return x.get == y.get; 932 } 933 } 934 935 unittest 936 { 937 import std.stdio; 938 stderr.writeln("unittest passed!"); 939 }