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<T>. 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()));
    }
}

1 Comment

Add a Comment

As it will appear on the website

Not displayed

Your website