Category Theory via C# (12) More Monoidal Functors: Lazy<>, Func<> And Nullable<>

[LINQ via C# series]

[Category Theory via C# series]

Latest version: https://weblogs.asp.net/dixin/category-theory-via-csharp-6-monoidal-functor-and-applicative-functor

Lazy<> monoidal functor

Lazy<> should be the simplest monoid functor - it is just the lazy version of Tuple<>. And in these posts it will be considered as the Id<> monoidal functor.

// [Pure]
public static partial class LazyExtensions
{
    public static Lazy<TResult> Apply<TSource, TResult>
        (this Lazy<Func<TSource, TResult>> selectorFunctor, Lazy<TSource> source) =>
            new Lazy<TResult>(() => selectorFunctor.Value(source.Value));

    public static Lazy<T> Lazy<T>
        (this T value) => new Lazy<T>(() => value);

    // φ: Lazy<Lazy<T1>, Lazy<T2>> => Lazy<Lazy<T1, T2>>
    public static Lazy<Lazy<T1, T2>> Binary<T1, T2>
        (this Lazy<Lazy<T1>, Lazy<T2>> binaryFunctor) =>
            new Func<T1, Func<T2, Lazy<T1, T2>>>(x => y => new Lazy<T1, T2>(x, y))
                .Lazy()
                .Apply(binaryFunctor.Value1)
                .Apply(binaryFunctor.Value2);

    // ι: Unit -> Lazy<Unit>
    public static Lazy<Unit> Unit
        (Unit unit) => unit.Lazy();
}

Tuple<> is similar to the Haskell Id Applicative. The usage will be demonstrated in later tests unit.

Func<> monoidal functor

Func<> is also monoidal functor:

// [Pure]
public static partial class FuncExtensions
{
    public static Func<TResult> Apply<TSource, TResult>
        (this Func<Func<TSource, TResult>> selectorFunctor, Func<TSource> source) =>
            () => selectorFunctor()(source());

    public static Func<T> Func<T>
        (this T value) => () => value;

    // φ: Lazy<Func<T1>, Func<T2>> => Func<Lazy<T1, T2>>
    public static Func<Lazy<T1, T2>> Binary<T1, T2>
        (this Lazy<Func<T1>, Func<T2>> binaryFunctor) =>
            FuncExtensions.Func(new Func<T1, Func<T2, Lazy<T1, T2>>>(x => y => new Lazy<T1, T2>(x, y)))
                .Apply(binaryFunctor.Value1)
                .Apply(binaryFunctor.Value2);

    // ι: Unit -> Func<Unit>
    public static Func<Unit> Unit
        (Unit unit) => unit.Func();
}

Nullable<> monoidal functor

Nullable<> created previously is monoidal functor too:

// [Pure]
public static partial class NullableExtensions
{
    public static Nullable<TResult> Apply<TSource, TResult>
        (this Nullable<Func<TSource, TResult>> selectorFunctor, Nullable<TSource> source) =>
            new Nullable<TResult>(() => selectorFunctor.HasValue && source.HasValue ?
                new Tuple<bool, TResult>(true, selectorFunctor.Value(source.Value)) :
                new Tuple<bool, TResult>(false, default(TResult)));

    public static Nullable<T> Nullable<T>
        (this T value) => new Nullable<T>(() => Tuple.Create(true, value));

    // φ: Lazy<Nullable<T1>, Nullable<T2>> => Nullable<Lazy<T1, T2>>
    public static Nullable<Lazy<T1, T2>> Binary<T1, T2>
        (this Lazy<Nullable<T1>, Nullable<T2>> binaryFunctor) =>
            new Func<T1, Func<T2, Lazy<T1, T2>>>(x => y => new Lazy<T1, T2>(x, y))
                .Nullable()
                .Apply(binaryFunctor.Value1)
                .Apply(binaryFunctor.Value2);

    // ι: Unit -> Nullable<Unit>
    public static Nullable<Unit> Unit
        (Unit unit) => unit.Nullable();
}

Unit tests

public partial class MonoidalFunctorTests
{
    [TestMethod()]
    public void LazyTest()
    {
        bool isExecuted1 = false;
        Func<int, int> addOne = x => { isExecuted1 = true; return x + 1; };
        Lazy<int> query1 = addOne.Lazy().Apply(2.Lazy());
        Assert.IsFalse(isExecuted1); // Laziness.
        Assert.AreEqual(2 + 1, query1.Value); // Execution.
        Assert.IsTrue(isExecuted1);

        // f.Functor().Apply(F) == F.Select(f)
        Assert.AreEqual(addOne.Lazy().Apply(1.Lazy()).Value, 1.Lazy().Select(addOne).Value);
        // id.Functor().Apply(F) == F
        Func<int, int> id = Functions.Id;
        Assert.AreEqual(id.Lazy().Apply(1.Lazy()).Value, 1.Lazy().Value);
        // o.Functor().Apply(F1).Apply(F2).Apply(F3) == F1.Apply(F2.Apply(F3))
        Func<int, int> addTwo = x => x + 2;
        Func<Func<int, int>, Func<Func<int, int>, Func<int, int>>> o =
            new Func<Func<int, int>, Func<int, int>, Func<int, int>>(FuncExtensions.o).Curry();
        Lazy<int> left1 = o.Lazy().Apply(addOne.Lazy()).Apply(addTwo.Lazy()).Apply(1.Lazy());
        Lazy<int> right1 = addOne.Lazy().Apply(addTwo.Lazy().Apply(1.Lazy()));
        Assert.AreEqual(left1.Value, right1.Value);
        // f.Functor().Apply(a.Functor()) == f(a).Functor()
        Assert.AreEqual(addOne.Lazy().Apply(1.Lazy()).Value, addOne(1).Lazy().Value);
        // F.Apply(a.Functor()) == (f => f(a)).Functor().Apply(F)
        Lazy<int> left2 = addOne.Lazy().Apply(1.Lazy());
        Lazy<int> right2 = new Func<Func<int, int>, int>(f => f(1)).Lazy().Apply(addOne.Lazy());
        Assert.AreEqual(left2.Value, right2.Value);
    }

    [TestMethod()]
    public void FuncTest()
    {
        bool isExecuted1 = false;
        Func<int, int> addOne = x => { isExecuted1 = true; return x + 1; };
        Func<int> query1 = FuncExtensions.Func(addOne).Apply(2.Func());
        Assert.IsFalse(isExecuted1); // Laziness.
        Assert.AreEqual(addOne(2), query1()); // Execution.
        Assert.IsTrue(isExecuted1);

        // f.Functor().Apply(F) == F.Select(f)
        Assert.AreEqual(FuncExtensions.Func(addOne).Apply(1.Func())(), 1.Func().Select(addOne)());
        // id.Functor().Apply(F) == F
        Func<int, int> id = Functions.Id;
        Assert.AreEqual(FuncExtensions.Func(id).Apply(1.Func())(), 1.Func()());
        // o.Functor().Apply(F1).Apply(F2).Apply(F3) == F1.Apply(F2.Apply(F3))
        Func<int, int> addTwo = x => x + 2;
        Func<Func<int, int>, Func<Func<int, int>, Func<int, int>>> o =
            new Func<Func<int, int>, Func<int, int>, Func<int, int>>(FuncExtensions.o).Curry();
        Func<int> left1 = FuncExtensions.Func(o).Apply(FuncExtensions.Func(addOne)).Apply(FuncExtensions.Func(addTwo)).Apply(1.Func());
        Func<int> right1 = FuncExtensions.Func(addOne).Apply(FuncExtensions.Func(addTwo).Apply(1.Func()));
        Assert.AreEqual(left1(), right1());
        // f.Functor().Apply(a.Functor()) == f(a).Functor()
        Assert.AreEqual(FuncExtensions.Func(addOne).Apply(1.Func())(), addOne(1).Func()());
        // F.Apply(a.Functor()) == (f => f(a)).Functor().Apply(F)
        Func<int> left2 = FuncExtensions.Func(addOne).Apply(1.Func());
        Func<int> right2 = FuncExtensions.Func(new Func<Func<int, int>, int>(f => f(1))).Apply(FuncExtensions.Func(addOne));
        Assert.AreEqual(left2(), right2());
    }

    [TestMethod()]
    public void FuncTest2()
    {
        bool isExecuted1 = false;
        Func<int, Func<int, string>> add = x => y =>
            { isExecuted1 = true; return (x + y).ToString(CultureInfo.InvariantCulture); };
        Func<string> query2 = FuncExtensions.Func(add).Apply(1.Func()).Apply(2.Func());
        Assert.IsFalse(isExecuted1); // Laziness.
        Assert.AreEqual(add(1)(2), query2()); // Execution.
        Assert.IsTrue(isExecuted1);

        // f.Functor().Apply(F) == F.Select(f)
        Assert.AreEqual(FuncExtensions.Func(add).Apply(1.Func())()(2), 1.Func().Select(add)()(2));
        // id.Functor().Apply(F) == F
        Func<int, int> id = Functions.Id;
        Assert.AreEqual(FuncExtensions.Func(id).Apply(1.Func())(), 1.Func()());
        // o.Functor().Apply(F1).Apply(F2).Apply(F3) == F1.Apply(F2.Apply(F3))
        Func<Func<int, string>, Func<int, int>> length = f => x => f(x).Length;
        Func<Func<Func<int, string>, Func<int, int>>, Func<Func<int, Func<int, string>>, Func<int, Func<int, int>>>> o =
            new Func<Func<Func<int, string>, Func<int, int>>, Func<int, Func<int, string>>, Func<int, Func<int, int>>>(FuncExtensions.o).Curry();
        Func<Func<int, int>> left1 = FuncExtensions.Func(o).Apply(FuncExtensions.Func(length)).Apply(FuncExtensions.Func(add)).Apply(1.Func());
        Func<Func<int, int>> right1 = FuncExtensions.Func(length).Apply(FuncExtensions.Func(add).Apply(1.Func()));
        Assert.AreEqual(left1()(2), right1()(2));
        // f.Functor().Apply(a.Functor()) == f(a).Functor()
        Assert.AreEqual(FuncExtensions.Func(add).Apply(1.Func())()(2), FuncExtensions.Func(add(1))()(2));
        // F.Apply(a.Functor()) == (f => f(a)).Functor().Apply(F)
        Func<Func<int, string>> left2 = FuncExtensions.Func(add).Apply(1.Func());
        Func<Func<int, string>> right2 = FuncExtensions.Func(new Func<Func<int, Func<int, string>>, Func<int, string>>(
                f => f(1))).Apply(FuncExtensions.Func(add));
        Assert.AreEqual(left2()(2), right2()(2));

        bool isExecuted3 = false;
        Func<string> consoleReadLine1 = () => "a";
        Func<string> consoleReadLine2 = () => "b";
        Func<string, Func<string, string>> concat = x => y =>
            { isExecuted3 = true; return string.Concat(x, y); };
        Func<string> concatLines = FuncExtensions.Func(concat).Apply(consoleReadLine1).Apply(consoleReadLine2);
        Assert.IsFalse(isExecuted3); // Laziness.
        Assert.AreEqual(string.Concat(consoleReadLine1(), consoleReadLine2()), concatLines());
        Assert.IsTrue(isExecuted3);
    }

    [TestMethod()]
    public void NullableTest()
    {
        bool isExecuted1 = false;
        Func<int, int> addOne = x => { isExecuted1 = true; return x + 1; };
        Nullable<int> query1 = addOne.Nullable().Apply(2.Nullable());
        Assert.IsFalse(isExecuted1); // Laziness.
        Assert.IsTrue(query1.HasValue); // Execution.
        Assert.AreEqual(addOne(2), query1.Value);
        Assert.IsTrue(isExecuted1);

        // f.Functor().Apply(F) == F.Select(f)
        Assert.AreEqual(addOne.Nullable().Apply(1.Nullable()).Value, 1.Nullable().Select(addOne).Value);
        // id.Functor().Apply(F) == F
        Func<int, int> id = Functions.Id;
        Assert.AreEqual(id.Nullable().Apply(1.Nullable()).Value, 1.Nullable().Value);
        // o.Functor().Apply(F1).Apply(F2).Apply(F3) == F1.Apply(F2.Apply(F3))
        Func<int, int> addTwo = x => x + 2;
        Func<Func<int, int>, Func<Func<int, int>, Func<int, int>>> o =
            new Func<Func<int, int>, Func<int, int>, Func<int, int>>(FuncExtensions.o).Curry();
        Nullable<int> left1 = o.Nullable().Apply(addOne.Nullable()).Apply(addTwo.Nullable()).Apply(1.Nullable());
        Nullable<int> right1 = addOne.Nullable().Apply(addTwo.Nullable().Apply(1.Nullable()));
        Assert.AreEqual(left1.Value, right1.Value);
        // f.Functor().Apply(a.Functor()) == f(a).Functor()
        Assert.AreEqual(addOne.Nullable().Apply(1.Nullable()).Value, addOne(1).Nullable().Value);
        // F.Apply(a.Functor()) == (f => f(a)).Functor().Apply(F)
        Nullable<int> left2 = addOne.Nullable().Apply(1.Nullable());
        Nullable<int> right2 = new Func<Func<int, int>, int>(f => f(1)).Nullable().Apply(addOne.Nullable());
        Assert.AreEqual(left2.Value, right2.Value);

        bool isExecuted2 = false;
        Func<int, int> addTwo2 = x => { isExecuted2 = true; return x + 2; };
        Nullable<int> query2 = addTwo2.Nullable().Apply(new Nullable<int>());
        Assert.IsFalse(isExecuted2); // Laziness.
        Assert.IsFalse(query2.HasValue); // Execution.
        Assert.IsFalse(isExecuted2);
    }
}

6 Comments

  • https://ma-study.blogspot.com/

  • When I read an article on this topic, Keonhacai the first thought was profound and difficult, and I wondered if others could understand.. My site has a discussion board for articles and photos similar to this topic. Could you please visit me when you have time to discuss this topic?

  • I've been searching for hours on this topic and finally found your post. I have read your post and I am very impressed. We prefer your opinion and will visit this site frequently to refer to your opinion. When would you like to visit my site?

  • Your writing is perfect and complete. <a href="https://google.gg/url?sa=t&url=https%3A%2F%2Fwww.mtclean.blog/">safetoto</a> However, I think it will be more wonderful if your post includes additional topics that I am thinking of. I have a lot of posts on my site similar to your topic. Would you like to visit once?

  • As I am looking at your writing, <a href="https://google.ge/url?sa=t&url=https%3A%2F%2Fwww.mtclean.blog/">casino online</a> I regret being unable to do outdoor activities due to Corona 19, and I miss my old daily life. If you also miss the daily life of those days, would you please visit my site once? My site is a site where I post about photos and daily life when I was free.

  • Looking at this article, I miss the time when I didn't wear a mask. <a href="https://google.ga/url?sa=t&url=https%3A%2F%2Fwww.mtclean.blog/">totosite</a> Hopefully this corona will end soon. My blog is a blog that mainly posts pictures of daily life before Corona and landscapes at that time. If you want to remember that time again, please visit us.

Add a Comment

As it will appear on the website

Not displayed

Your website