I'm trying to write a general monoidal pattern in C#, starting with a homogeneous function combining two non-null values, and returning either value if the other is null, or the combined value if neither is, or null. So I have this:
public static Func<TIn?, TIn?,TIn?> Drop2<TIn>(Func<TIn, TIn, TIn> f)
{
return (lhs, rhs) =>
{
if (lhs == null && rhs == null)
{
return default;
}
if (lhs == null && rhs != null)
{
return rhs;
}
if (rhs == null && lhs != null)
{
return lhs;
}
return f(lhs, rhs);
};
}
This looks fine and it even compiles, but when I try to use it, two odd things happen.
Func<int, int, int> sum = (lhs, rhs) => lhs rhs;
var sumNonNull = DropNullable.Drop2(sum);
The Intellisense for sumNonNull shows as Func<int, int, int>?
, not the expected Func<int?, int?, int?>
, and I can't pass in null as either argument for sumNonNull (can't convert from int? to int).
Should this work? What am I missing?
Thanks in advance for any help
CodePudding user response:
I think you want something like this:
public static Func<TIn?, TIn?, TIn?> Drop2<TIn>(Func<TIn?, TIn?, TIn?> f)
{
return (lhs, rhs) =>
lhs is null ? rhs
: rhs is null ? lhs
: f(lhs, rhs);
}
note TIn?
in both input argument and the result (both of them can be null
). Then you can use it as follow:
// note int? - sumInt accepts nulls (int? is a short for Nullable<int>)
Func<int?, int?, int?> sumInt = (a, b) => a b;
Console.WriteLine(Drop2(sumInt)(null, 123));
Console.WriteLine(Drop2(sumInt)(456, null));
Console.WriteLine(Drop2(sumInt)(456, 123));
Console.WriteLine(Drop2(sumInt)(null, null));
// strings can be null
Func<string, string, string> sumStr = (a, b) => a b;
Console.WriteLine(Drop2(sumStr)(null, "123"));
Console.WriteLine(Drop2(sumStr)("456", null));
Console.WriteLine(Drop2(sumStr)("456", "123"));
Console.WriteLine(Drop2(sumStr)(null, null));