1 /**
2 *   Copyright: © 2014 Anton Gushcha
3 *   License: Subject to the terms of the MIT license, as written in the included LICENSE file.
4 *   Authors: NCrashed <ncrashed@gmail.com>
5 */
6 module dcheck.maybe;
7 
8 import std.traits;
9 import std.exception;
10 
11 /**
12 *   Struct-wrapper to handle result of computations,
13 *   that can fail.
14 */
15 struct Maybe(T)
16     if(is(T == class) || is(T == interface) 
17         || isPointer!T || isArray!T) 
18 {
19     private T value;
20     
21     /// Alias to stored type
22     alias T StoredType;
23     
24     /**
25     *   Constructing Maybe from $(B value).
26     *   If pointer is $(B null) methods: $(B isNothing) returns true and 
27     *   $(B get) throws Error.
28     */
29     this(T value) pure
30     {
31         this.value = value;
32     }
33     
34     /**
35     *   Constructing empty Maybe.
36     *   If Maybe is created with the method, it is considred empty
37     *   and $(B isNothing) returns false.
38     */
39     static Maybe!T nothing()
40     {
41         return Maybe!T(null);
42     } 
43     
44     /// Returns true if stored value is null
45     bool isNothing() const
46     {
47         return value is null;
48     }
49     
50     /**
51     *   Unwrap value from Maybe.
52     *   If stored value is $(B null), Error is thrown.
53     */
54     T get()
55     {
56         assert(value !is null, "Stored reference is null!");
57         return value;
58     }
59     
60     /**
61     *   Unwrap value from Maybe.
62     *   If stored value is $(B null), Error is thrown.
63     */
64     const(T) get() const
65     {
66         assert(value !is null, "Stored reference is null!");
67         return value;
68     }
69     
70     /**
71     *   If struct holds $(B null), then $(B nothingCase) result
72     *   is returned. If struct holds not $(B null) value, then
73     *   $(justCase) result is returned. $(B justCase) is fed
74     *   with unwrapped value.
75     */
76     U map(U)(U delegate() nothingCase, U delegate(T) justCase)
77     {
78         return isNothing ? nothingCase() : justCase(value);
79     }
80     
81     /**
82     *   If struct holds $(B null), then $(B nothingCase) result
83     *   is returned. If struct holds not $(B null) value, then
84     *   $(justCase) result is returned. $(B justCase) is fed
85     *   with unwrapped value.
86     */
87     U map(U)(U delegate() nothingCase, U delegate(const T) justCase) const
88     {
89         return isNothing ? nothingCase() : justCase(value);
90     }
91 }
92 /// Example
93 unittest
94 {
95     class A {}
96     
97     auto a = new A();
98     auto ma = Maybe!A(a);
99     auto mb = Maybe!A(null);
100     
101     assert(!ma.isNothing);
102     assert(mb.isNothing);
103     
104     assert(ma.get == a);
105     assertThrown!Error(mb.get);
106     
107     bool ncase = false, jcase = false;
108     ma.map(() {ncase = true;}, (v) {jcase = true;});
109     assert(jcase && !ncase);
110     
111     ncase = jcase = false;
112     mb.map(() {ncase = true;}, (v) {jcase = true;});
113     assert(!jcase && ncase);
114 }
115 
116 /**
117 *   Struct-wrapper to handle result of computations,
118 *   that can fail.
119 */
120 struct Maybe(T)
121     if(is(T == struct) || isAssociativeArray!T || isBasicType!T) 
122 {
123     private bool empty;
124     private T value;
125     
126     /// Alias to stored type
127     alias T StoredType;
128     
129     /**
130     *   Constructing empty Maybe.
131     *   If Maybe is created with the method, it is considred empty
132     *   and $(B isNothing) returns false.
133     */
134     static Maybe!T nothing()
135     {
136         Maybe!T ret;
137         ret.empty = true;
138         return ret;
139     } 
140     
141     /**
142     *   Constructing Maybe from $(B value).
143     *   If Maybe is created with the constructor, it is considered non empty
144     *   and $(B isNothing) returns false.
145     */
146     this(T value) pure
147     {
148         this.value = value;
149         empty = false;
150     }
151     
152     /// Returns true if stored value is null
153     bool isNothing() const
154     {
155         return empty;
156     }
157     
158     /**
159     *   Unwrap value from Maybe.
160     *   If the Maybe is empty, Error is thrown.
161     */
162     T get()
163     {
164         assert(!empty, "Stored value is null!");
165         return value;
166     }
167     
168     /**
169     *   Unwrap value from Maybe.
170     *   If the Maybe is empty, Error is thrown.
171     */
172     const(T) get() const
173     {
174         assert(!empty, "Stored value is null!");
175         return value;
176     }
177     
178     /**
179     *   If struct holds $(B null), then $(B nothingCase) result
180     *   is returned. If struct holds not $(B null) value, then
181     *   $(justCase) result is returned. $(B justCase) is fed
182     *   with unwrapped value.
183     */
184     U map(U)(U delegate() nothingCase, U delegate(T) justCase)
185     {
186         return isNothing ? nothingCase() : justCase(value);
187     }
188     
189     /**
190     *   If struct holds $(B null), then $(B nothingCase) result
191     *   is returned. If struct holds not $(B null) value, then
192     *   $(justCase) result is returned. $(B justCase) is fed
193     *   with unwrapped value.
194     */
195     U map(U)(U delegate() nothingCase, U delegate(const T) justCase) const
196     {
197         return isNothing ? nothingCase() : justCase(value);
198     }
199 }
200 /// Example
201 unittest
202 {
203     struct A {}
204     
205     auto ma = Maybe!A(A());
206     auto mb = Maybe!A.nothing;
207     
208     assert(!ma.isNothing);
209     assert(mb.isNothing);
210     
211     assert(ma.get == A());
212     assertThrown!Error(mb.get);
213     
214     bool ncase = false, jcase = false;
215     ma.map(() {ncase = true;}, (v) {jcase = true;});
216     assert(jcase && !ncase);
217     
218     ncase = jcase = false;
219     mb.map(() {ncase = true;}, (v) {jcase = true;});
220     assert(!jcase && ncase);
221 }