906 words
5 minutes
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);
}
}
Category Theory via C# (12) More Monoidal Functors: Lazy<>, Func<> And Nullable<>
https://dixin.github.io/posts/category-theory-via-c-sharp-12-more-monoidal-functors-lazy-func-and-nullable/
Author
Dixin
Published at
2018-12-13
License
CC BY-NC-SA 4.0