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 }