Created
October 30, 2014 14:53
-
-
Save Biotronic/253ee5eee8623f9471dd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import std.typecons; | |
import std.traits; | |
import std.typetuple; | |
import std.stdio : writeln; | |
struct Maybe(T) { | |
T value = void; | |
bool b = true; | |
@property static Maybe None() { | |
Maybe result; | |
result.b = false; | |
return result; | |
} | |
} | |
bool isNone(T)(T a) if (isMaybe!T) { | |
return !a.b; | |
} | |
bool isNone(T)(T a) if (!isMaybe!T) { | |
return false; | |
} | |
enum isMaybe(T) = is(T == Maybe!U, U); | |
unittest { | |
assert(isMaybe!(Maybe!int)); | |
assert(!isMaybe!string); | |
} | |
template staticMap(alias Fn) { | |
alias staticMap = TypeTuple!(); | |
} | |
template staticMap(alias Fn, T...) { | |
alias staticMap = TypeTuple!(Fn!(T[0]), staticMap!(Fn, T[1..$])); | |
} unittest { | |
assert([staticMap!(isMaybe, int, string, Maybe!float)] == [false, false, true]); | |
} | |
template canCall(alias Fn, Args...) { | |
enum canCall = __traits(compiles, (Args args)=>Fn(args)); | |
} unittest { | |
assert(canCall!(main)); | |
assert(canCall!((int a, float b)=>a+b, int, float)); | |
assert(canCall!((int a, float b)=>a+b, int, int)); | |
} | |
template RemoveMaybe(T) { | |
static if (is(T == Maybe!U, U)) { | |
alias RemoveMaybe = U; | |
} else { | |
alias RemoveMaybe = T; | |
} | |
} unittest { | |
assert(is(RemoveMaybe!int == int)); | |
assert(is(RemoveMaybe!(Maybe!int) == int)); | |
} | |
template goodArgs(alias Fn, Args...) { | |
enum goodArgs = canCall!(Fn, staticMap!(RemoveMaybe, Args)); | |
} | |
auto unsafeFetchFromMaybe(T)(T value) if (isMaybe!T) { | |
return value.value; | |
} | |
T unsafeFetchFromMaybe(T)(T value) if (!isMaybe!T) { | |
return value; | |
} | |
template tupleMap(alias Fn) { | |
auto tupleMap(Args...)(Args args) { | |
static if (Args.length > 1) { | |
return tuple(Fn(args[0]), tupleMap(args[1..$]).expand); | |
} else { | |
return tuple(Fn(args[0])); | |
} | |
} | |
} unittest { | |
assert(tupleMap!(a=>a+1)(1, 2.4, 3.5f) == tuple(2, 3.4, 4.5f)); | |
} | |
bool anyNone(Args...)(Args args) { | |
static if (Args.length == 0) { | |
return false; | |
} else if (isNone(args[0])) { | |
return true; | |
} else { | |
return anyNone(args[1..$]); | |
} | |
} unittest { | |
assert(!anyNone()); | |
assert(!anyNone(3)); | |
assert(!anyNone("None")); | |
assert(anyNone(Maybe!int.None)); | |
} | |
template maybe(alias Fn) { | |
auto maybe(Args...)(Args args) if (goodArgs!(Fn, Args)) { | |
alias TResult = ReturnType!(typeof((staticMap!(RemoveMaybe, Args) cleanedArgs) {return Fn(cleanedArgs);})); | |
if (anyNone(args)) { | |
return Maybe!TResult.None; | |
} else { | |
auto fixedArgs = tupleMap!(unsafeFetchFromMaybe)(args).expand; | |
return Maybe!(TResult)(Fn(fixedArgs)); | |
} | |
} | |
} | |
int add(int a, int b) { | |
return a + b; | |
} | |
void main() { | |
auto a = maybe!add(Maybe!int.None, 3); | |
assert(isNone(a)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment