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 }