756 words
4 minutes
Lambda Calculus via C# (10) Church Numeral Arithmetic Operators

[LINQ via C# series]#

[Lambda Calculus via C# series]#

Latest version: https://weblogs.asp.net/dixin/lambda-calculus-via-csharp-3-numeral-arithmetic-and-predicate#

Operators#

Another benefits of introducing (cheating with) _Numeral class into lambda calculus is - it provides a place to define custom operators.

public partial class _Numeral
{
public static _Numeral operator +
(_Numeral a, _Numeral b) => a.Add(b);
public static _Numeral operator -
(_Numeral a, _Numeral b) => a.Subtract(b);
public static _Numeral operator *
(_Numeral a, _Numeral b) => a.Multiply(b);
public static _Numeral operator ^
(_Numeral a, _Numeral b) => a.Pow(b);
public static _Numeral operator ++
(_Numeral numeral) => numeral.Increase();
public static _Numeral operator --
(_Numeral numeral) => numeral.Decrease();
}

This cannot be done to delegate type Numeral. In C#, custom operators cannot be defined for delegates/functions/lambda expressions.

Now Church numerals and arithmetic operations are all implemented in C#. Now it’s time for testing.

Conversion between Church numeral (now _Numeral) and System.UInt32#

Similar to Church Boolean <-> System.Boolean, some conversion helper methods can be created between _Numeral and System.UInt32:

public static partial class ChurchEncoding
{
public static _Numeral _Church
(this uint n) => n > 0 ? new _Numeral(_Church(n - 1)) : _Numeral.Zero;
public static uint _Unchurch
(this _Numeral numeral) => numeral.Numeral<uint>()(x => x + 1)(0);
}

Once again, these 2 methods are tagged with underscore because unit is C# specific.

In _Unchurch, a Church numeral (now a _Numeral) n is converted to natural number by “applying add 1” n times on 0.

Similarly to _Unchurch, _Numeral can be converted to string too:

public static partial class ChurchEncoding
{
public static string _Visualize(this _Numeral numeral)
{
return numeral.Numeral<string>()(x => string.Concat(x, "#"))(string.Empty);
}
}

0 will be converted to empty string, 1 will be “#”, 2 will be “##”, etc.

Compare _Numeral and System.UInt32#

Similar to above operators, == and != can be defined between Church numeral and System.UInt32:

public partial class _Numeral
{
public static bool operator ==
(_Numeral a, uint b) => a._Unchurch() == b;
public static bool operator ==
(uint a, _Numeral b) => a == b._Unchurch();
public static bool operator !=
(_Numeral a, uint b) => a._Unchurch() != b;
public static bool operator !=
(uint a, _Numeral b) => a != b._Unchurch();
}

bool and uint - these are totally C# specific, and will be only used for unit tests.

Unit tests#

The last function needed is a Pow function for uint, because .NET only has a Math.Pow function for double.

public static class UInt32Extensions
{
public static uint Pow(this uint mantissa, uint exponent)
{
uint result = 1;
for (int i = 0; i < exponent; i++)
{
result *= mantissa;
}
return result;
}
}

The same way as Church Boolean tests, Church numeral and arithmetic operation can be unit tested by directly comparing results with System.UInt32’s arithmetic operation results:

[TestClass()]
public class _NumeralExtensionsTests
{
[TestMethod()]
public void IncreaseTest()
{
_Numeral numeral = 0U._Church();
Assert.IsTrue(0U + 1U == ++numeral);
Assert.IsTrue(1U + 1U == ++numeral);
Assert.IsTrue(2U + 1U == ++numeral);
Assert.IsTrue(3U + 1U == ++numeral);
numeral = 123U._Church();
Assert.IsTrue(123U + 1U == ++numeral);
}
[TestMethod()]
public void AddTest()
{
Assert.IsTrue(0U + 0U == 0U._Church() + 0U._Church());
Assert.IsTrue(0U + 1U == 0U._Church() + 1U._Church());
Assert.IsTrue(10U + 0U == 10U._Church() + 0U._Church());
Assert.IsTrue(0U + 10U == 0U._Church() + 10U._Church());
Assert.IsTrue(1U + 1U == 1U._Church() + 1U._Church());
Assert.IsTrue(10U + 1U == 10U._Church() + 1U._Church());
Assert.IsTrue(1U + 10U == 1U._Church() + 10U._Church());
Assert.IsTrue(3U + 5U == 3U._Church() + 5U._Church());
Assert.IsTrue(123U + 345U == 123U._Church() + 345U._Church());
}
[TestMethod()]
public void DecreaseTest()
{
_Numeral numeral = 3U._Church();
Assert.IsTrue(3U - 1U == --numeral);
Assert.IsTrue(2U - 1U == --numeral);
Assert.IsTrue(1U - 1U == --numeral);
Assert.IsTrue(0U == --numeral);
numeral = 123U._Church();
Assert.IsTrue(123U - 1U == --numeral);
}
[TestMethod()]
public void SubtractTest()
{
Assert.IsTrue(0U - 0U == 0U._Church() - 0U._Church());
Assert.IsTrue(0U == 0U._Church() - 1U._Church());
Assert.IsTrue(10U - 0U == 10U._Church() - 0U._Church());
Assert.IsTrue(0U == 0U._Church() - 10U._Church());
Assert.IsTrue(1U - 1U == 1U._Church() - 1U._Church());
Assert.IsTrue(10U - 1U == 10U._Church() - 1U._Church());
Assert.IsTrue(0U == 1U._Church() - 10U._Church());
Assert.IsTrue(0U == 3U._Church() - 5U._Church());
Assert.IsTrue(0U == 123U._Church() - 345U._Church());
}
[TestMethod()]
public void MultiplyTest()
{
Assert.IsTrue(0U * 0U == 0U._Church() * 0U._Church());
Assert.IsTrue(0U * 1U == 0U._Church() * 1U._Church());
Assert.IsTrue(10U * 0U == 10U._Church() * 0U._Church());
Assert.IsTrue(0U * 10U == 0U._Church() * 10U._Church());
Assert.IsTrue(1U * 1U == 1U._Church() * 1U._Church());
Assert.IsTrue(10U * 1U == 10U._Church() * 1U._Church());
Assert.IsTrue(1U * 10U == 1U._Church() * 10U._Church());
Assert.IsTrue(3U * 5U == 3U._Church() * 5U._Church());
Assert.IsTrue(12U * 23U == 12U._Church() * 23U._Church());
}
[TestMethod()]
public void PowTest()
{
Assert.IsTrue(0U.Pow(1U) == (0U._Church() ^ 1U._Church()));
Assert.IsTrue(10U.Pow(0U) == (10U._Church() ^ 0U._Church()));
Assert.IsTrue(0U.Pow(10U) == (0U._Church() ^ 10U._Church()));
Assert.IsTrue(1U.Pow(1U) == (1U._Church() ^ 1U._Church()));
Assert.IsTrue(10U.Pow(1U) == (10U._Church() ^ 1U._Church()));
Assert.IsTrue(1U.Pow(10U) == (1U._Church() ^ 10U._Church()));
Assert.IsTrue(3U.Pow(5U) == (3U._Church() ^ 5U._Church()));
Assert.IsTrue(5U.Pow(3U) == (5U._Church() ^ 3U._Church()));
}
}
Lambda Calculus via C# (10) Church Numeral Arithmetic Operators
https://dixin.github.io/posts/lambda-calculus-via-c-sharp-10-church-numeral-arithmetic-operators/
Author
Dixin
Published at
2018-11-10
License
CC BY-NC-SA 4.0