@@ -1074,3 +1074,105 @@ pure nothrow @system unittest
1074
1074
arr.ptr++
1075
1075
));
1076
1076
}
1077
+
1078
+ /**
1079
+ Unpacks the content of a `Nullable`, performs an operation and packs it again. Does nothing if isNull.
1080
+
1081
+ When called on a `Nullable`, `apply` will unpack the value contained in the `Nullable`,
1082
+ pass it to the function you provide and wrap the result in another `Nullable` (if necessary).
1083
+ If the Nullable is null, `apply` will return null itself.
1084
+
1085
+ Params:
1086
+ t = a `Nullable`
1087
+ fun = a function operating on the content of the nullable
1088
+
1089
+ Returns:
1090
+ `fun(t.get).nullable` if `!t.isNull`, else `Nullable.init`.
1091
+
1092
+ See also:
1093
+ $(HTTP en.wikipedia.org/wiki/Monad_(functional_programming)#The_Maybe_monad, The `Maybe` monad)
1094
+ */
1095
+ template apply (alias fun)
1096
+ {
1097
+ import std.functional : unaryFun;
1098
+ import std.typecons : Nullable, nullable;
1099
+
1100
+ auto apply (T)(T t)
1101
+ if (isInstanceOf! (Nullable, T) && is (typeof (unaryFun! fun(T.init.get ))))
1102
+ {
1103
+ alias FunType = typeof (unaryFun! fun(T.init.get ));
1104
+
1105
+ enum MustWrapReturn = ! isInstanceOf! (Nullable, FunType);
1106
+
1107
+ static if (MustWrapReturn)
1108
+ {
1109
+ alias ReturnType = Nullable! FunType;
1110
+ }
1111
+ else
1112
+ {
1113
+ alias ReturnType = FunType;
1114
+ }
1115
+
1116
+ if (! t.isNull)
1117
+ {
1118
+ static if (MustWrapReturn)
1119
+ {
1120
+ return fun (t.get ).nullable;
1121
+ }
1122
+ else
1123
+ {
1124
+ return fun (t.get );
1125
+ }
1126
+ }
1127
+ else
1128
+ {
1129
+ return ReturnType.init;
1130
+ }
1131
+ }
1132
+ }
1133
+
1134
+ // /
1135
+ nothrow pure @nogc @safe unittest
1136
+ {
1137
+ import std.typecons : Nullable;
1138
+
1139
+ alias toFloat = i => cast (float ) i;
1140
+
1141
+ Nullable! int sample;
1142
+
1143
+ // apply(null) results in a null `Nullable` of the function's return type.
1144
+ Nullable! float f = sample.apply! toFloat;
1145
+ assert (sample.isNull && f.isNull);
1146
+
1147
+ sample = 3 ;
1148
+
1149
+ // apply(non-null) calls the function and wraps the result in a `Nullable`.
1150
+ f = sample.apply! toFloat;
1151
+ assert (! sample.isNull && ! f.isNull);
1152
+ assert (f.get == 3.0f );
1153
+ }
1154
+
1155
+ // /
1156
+ nothrow pure @nogc @safe unittest
1157
+ {
1158
+ import std.typecons : Nullable, nullable;
1159
+
1160
+ alias greaterThree = i => (i > 3 ) ? i.nullable : Nullable! (typeof (i)).init;
1161
+
1162
+ Nullable! int sample;
1163
+
1164
+ // when the function already returns a `Nullable`, that `Nullable` is not wrapped.
1165
+ auto result = sample.apply! greaterThree;
1166
+ assert (sample.isNull && result.isNull);
1167
+
1168
+ // The function may decide to return a null `Nullable`.
1169
+ sample = 3 ;
1170
+ result = sample.apply! greaterThree;
1171
+ assert (! sample.isNull && result.isNull);
1172
+
1173
+ // Or it may return a value already wrapped in a `Nullable`.
1174
+ sample = 4 ;
1175
+ result = sample.apply! greaterThree;
1176
+ assert (! sample.isNull && ! result.isNull);
1177
+ assert (result.get == 4 );
1178
+ }
0 commit comments