Home > Software engineering >  'Lifting' a C# function as a monoidal fails and changes signature
'Lifting' a C# function as a monoidal fails and changes signature

Time:11-08

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));
  • Related