1 module universal.extras.maybe; 2 3 import universal.meta; 4 import universal.core.coproduct; 5 import universal.core.product; 6 import universal.core.apply; 7 import std.traits; 8 import std.typetuple; 9 10 /* 11 a type which may or may not be inhabited 12 */ 13 struct Maybe(A) 14 { 15 mixin UnionInstance!( 16 q{nothing}, 17 q{just}, A, 18 ); 19 } 20 alias Maybe(A : void) = Maybe!(Unit); 21 22 Maybe!A just(A)(A a) { return Maybe!A().just(a); } 23 Maybe!A nothing(A)() { return Maybe!A().nothing; } 24 25 auto isNothing(A)(Maybe!A ma) { return ma.isCase!q{nothing}; } 26 auto isJust (A)(Maybe!A ma) { return ma.isCase!q{just}; } 27 28 /* 29 apply f to a Maybe if it is inhabited, otherwise return a default value. 30 If f returns void, unit is returned regardless 31 */ 32 template maybe(alias f) 33 { 34 template maybe(A, B = typeof(A.init.apply!f)) 35 { 36 B maybe(Maybe!A m, B b = B.init) 37 { 38 return m.visit!( 39 q{just}, (a,_) => a.apply!f, 40 q{nothing}, (b) => b, 41 )(b); 42 } 43 } 44 } 45 46 /* 47 fmap 48 */ 49 template maybeMap(alias f) 50 { 51 template maybeMap(A) 52 { 53 alias B = typeof(A.init.apply!f); 54 55 Maybe!B maybeMap(Maybe!A m) 56 { 57 if(m.isJust) 58 return just(f(m.just)); 59 else 60 return nothing!B; 61 } 62 } 63 } 64 65 /* 66 like visit, but not all cases need to be accounted for. Requires named fields. If one of the supplied function matches the inhabited union, it returns wrapped in "just". If none match, it returns "nothing". 67 */ 68 template maybeVisit(dtors...) 69 { 70 auto maybeVisit(U)(U u) 71 if(is(U.Union)) 72 { 73 alias dtorNames = Filter!(isString, dtors); 74 alias dtorFuncs = Filter!(isParameterized, dtors); 75 76 import std.algorithm.searching : countUntil; 77 78 enum ctorIdx(string name) = [U.Union.ctorNames].countUntil!(n => n == name); 79 enum dtorIdx(string name) = [dtorNames].countUntil!(n => n == name); 80 81 alias DtorCod(string name) 82 = typeof(dtorFuncs[dtorIdx!name](U.Union.Args!(ctorIdx!name).init)); 83 84 alias B = Universal!(CommonType!(staticMap!(DtorCod, dtorNames))); 85 86 template doVisit(string name) 87 { 88 enum i = dtorIdx!name; 89 enum j = ctorIdx!name; 90 91 static if(i > -1) 92 auto doVisit(U.Union.Args!j a) 93 { return a.apply!(dtorFuncs[i]).apply!(just!B); } 94 else 95 auto doVisit(U.Union.Args!j) 96 { return nothing!B; } 97 98 } 99 100 return u.visit!(staticMap!(doVisit, U.Union.ctorNames)); 101 } 102 } 103 104 @("EXAMPLES") unittest 105 { 106 Union!("a", int, "b", int) u; 107 u.b = 3; 108 109 auto m = u.maybeVisit!(q{a}, a => a); 110 assert(m.isNothing); 111 112 m = u.maybeVisit!(q{b}, b => b); 113 assert(m == just(3)); 114 }