Skip to content

Commit 0c9a10b

Browse files
authored
Merge pull request #6182 from FeepingCreature/feature/nullable-bind
Implement apply for Nullable, in analogy to Haskell's monad operation merged-on-behalf-of: Jack Stouffer <[email protected]>
2 parents 0ebf719 + 057386f commit 0c9a10b

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
`apply` was added to `std.typecons`.
2+
3+
`apply` is an operation for $(REF Nullable, std, typecons) values that "unpacks" the `Nullable`, performs some
4+
operation (that is passed as a template parameter), then packs the result into another `Nullable` if necessary.
5+
When the initial `Nullable` is `null`, the resulting `Nullable` is also `null` and the function is not
6+
called.
7+
8+
-----
9+
Nullable!int n;
10+
alias square = i => i * i;
11+
n = n.apply!square; // does nothing if isNull
12+
assert(n.isNull);
13+
n = 2;
14+
assert(n.apply!square.get == 4);
15+
-----

std/typecons.d

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3401,6 +3401,102 @@ if (is (typeof(nullValue) == T))
34013401
assert(ntts.to!string() == "2.5");
34023402
}
34033403

3404+
/**
3405+
Unpacks the content of a $(D Nullable), performs an operation and packs it again. Does nothing if isNull.
3406+
3407+
When called on a $(D Nullable), $(D apply) will unpack the value contained in the $(D Nullable),
3408+
pass it to the function you provide and wrap the result in another $(D Nullable) (if necessary).
3409+
If the Nullable is null, $(D apply) will return null itself.
3410+
3411+
Params:
3412+
t = a $(D Nullable)
3413+
fun = a function operating on the content of the nullable
3414+
3415+
Returns:
3416+
`fun(t.get).nullable` if `!t.isNull`, else `Nullable.init`.
3417+
3418+
See also:
3419+
$(HTTP en.wikipedia.org/wiki/Monad_(functional_programming)#The_Maybe_monad, The `Maybe` monad)
3420+
*/
3421+
template apply(alias fun)
3422+
{
3423+
import std.functional : unaryFun;
3424+
3425+
auto apply(T)(T t)
3426+
if (isInstanceOf!(Nullable, T) && is(typeof(unaryFun!fun(T.init.get))))
3427+
{
3428+
alias FunType = typeof(unaryFun!fun(T.init.get));
3429+
3430+
enum MustWrapReturn = !isInstanceOf!(Nullable, FunType);
3431+
3432+
static if (MustWrapReturn)
3433+
{
3434+
alias ReturnType = Nullable!FunType;
3435+
}
3436+
else
3437+
{
3438+
alias ReturnType = FunType;
3439+
}
3440+
3441+
if (!t.isNull)
3442+
{
3443+
static if (MustWrapReturn)
3444+
{
3445+
return fun(t.get).nullable;
3446+
}
3447+
else
3448+
{
3449+
return fun(t.get);
3450+
}
3451+
}
3452+
else
3453+
{
3454+
return ReturnType.init;
3455+
}
3456+
}
3457+
}
3458+
3459+
///
3460+
nothrow pure @nogc @safe unittest
3461+
{
3462+
alias toFloat = i => cast(float) i;
3463+
3464+
Nullable!int sample;
3465+
3466+
// apply(null) results in a null $(D Nullable) of the function's return type.
3467+
Nullable!float f = sample.apply!toFloat;
3468+
assert(sample.isNull && f.isNull);
3469+
3470+
sample = 3;
3471+
3472+
// apply(non-null) calls the function and wraps the result in a $(D Nullable).
3473+
f = sample.apply!toFloat;
3474+
assert(!sample.isNull && !f.isNull);
3475+
assert(f.get == 3.0f);
3476+
}
3477+
3478+
///
3479+
nothrow pure @nogc @safe unittest
3480+
{
3481+
alias greaterThree = i => (i > 3) ? i.nullable : Nullable!(typeof(i)).init;
3482+
3483+
Nullable!int sample;
3484+
3485+
// when the function already returns a $(D Nullable), that $(D Nullable) is not wrapped.
3486+
auto result = sample.apply!greaterThree;
3487+
assert(sample.isNull && result.isNull);
3488+
3489+
// The function may decide to return a null $(D Nullable).
3490+
sample = 3;
3491+
result = sample.apply!greaterThree;
3492+
assert(!sample.isNull && result.isNull);
3493+
3494+
// Or it may return a value already wrapped in a $(D Nullable).
3495+
sample = 4;
3496+
result = sample.apply!greaterThree;
3497+
assert(!sample.isNull && !result.isNull);
3498+
assert(result.get == 4);
3499+
}
34043500

34053501
/**
34063502
Just like $(D Nullable!T), except that the object refers to a value

0 commit comments

Comments
 (0)