1 /**
2 Copyright: Copyright (c) 2020, Joakim Brännström. All rights reserved.
3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 Definition of an optional type using sumtype.
7 */
8 module my.optional;
9 
10 import std.traits : isSomeFunction, ReturnType;
11 
12 import sumtype;
13 
14 alias Optional(T) = SumType!(None, Some!T);
15 
16 /// Optional with no value
17 Optional!T none(T)() {
18     return typeof(return)(None.init);
19 }
20 
21 /// An optional containing a value.
22 Optional!T some(T)(T value) {
23     return typeof(return)(Some!T(value));
24 }
25 
26 /// No value in the Optional.
27 struct None {
28 }
29 
30 /// A value.
31 struct Some(T) {
32     T value;
33     alias value this;
34 }
35 
36 bool hasValue(T : SumType!(None, Some!U), U)(T v) {
37     return match!((None a) => false, (Some!U a) => true)(v);
38 }
39 
40 U orElse(T, U)(T v, U or) if (is(T == Optional!U)) {
41     return match!((None a) => or, (Some!U a) => a.value)(v);
42 }
43 
44 T orElse(T : SumType!(None, Some!U), U)(T v, T or) {
45     return match!((None a) => or, (Some!U a) => T(a))(v);
46 }
47 
48 T orElse(T : SumType!(None, Some!U), U, OrT)(T v, OrT or)
49         if (isSomeFunction!OrT && is(ReturnType!OrT == T)) {
50     return match!((None a) => or(), (Some!U a) => T(a))(v);
51 }
52 
53 U orElse(T : SumType!(None, Some!U), U, OrT)(T v, OrT or)
54         if (isSomeFunction!OrT && is(ReturnType!OrT == U)) {
55     return match!((None a) => or(), (Some!U a) => a.value)(v);
56 }
57 
58 @("shall chain multiple optional")
59 unittest {
60     static int fn1() {
61         return 5;
62     }
63 
64     static Optional!int fn2() {
65         return some(5);
66     }
67 
68     assert(none!int.hasValue == false);
69     assert(some(5).hasValue == true);
70 
71     assert(none!int.orElse(5) == 5);
72     assert(none!int.orElse(() => 5) == 5);
73     assert(none!int.orElse(&fn1) == 5);
74 
75     assert(some(10).orElse(5) == 10);
76     assert(some(10).orElse(() => 5) == 10);
77     assert(some(10).orElse(&fn1) == 10);
78 
79     assert(none!int.orElse(some(5)) == some(5));
80     assert(none!int.orElse(() => some(5)) == some(5));
81     assert(none!int.orElse(&fn2) == some(5));
82 
83     assert(some(10).orElse(some(5)) == some(10));
84     assert(some(10).orElse(() => some(5)) == some(10));
85     assert(some(10).orElse(&fn2) == some(10));
86 }