Hacker News new | ask | show | jobs
by louthy 3294 days ago
Here's an ad-hoc polymorphic way of dealing with numeric types in C# without the boxing that would be caused by using an IArithmetic interface:

    public interface Num<A>
    {
        A Add(A x, A y);
        A Subtract(A x, A y);
        A Multiply(A x, A y);
        A Divide(A x, A y);
        A FromInteger(int value);
    }

    public struct NumInt : Num<int>
    {
        public int Add(int x, int y) => x + y;
        public int Subtract(int x, int y) => x - y;
        public int Multiply(int x, int y) => x * y;
        public int Divide(int x, int y) => x / y;
        public int FromInteger(int value) => value;
    }

    public struct NumDouble : Num<double>
    {
        public double Add(double x, double y) => x + y;
        public double Subtract(double x, double y) => x - y;
        public double Multiply(double x, double y) => x * y;
        public double Divide(double x, double y) => x / y;
        public double FromInteger(int value) => (double)value;
    }

    public struct NumBigInt : Num<BigInteger>
    {
        public BigInteger Add(BigInteger x, BigInteger y) => x + y;
        public BigInteger Subtract(BigInteger x, BigInteger y) => x - y;
        public BigInteger Multiply(BigInteger x, BigInteger y) => x * y;
        public BigInteger Divide(BigInteger x, BigInteger y) => x / y;
        public BigInteger FromInteger(int value) => (BigInteger)value;
    }

    public static class TestGenericNums
    {
        public static void Test()
        {
            var a = DoubleIt<NumInt, int>(10);  // 20
            var b = SquareIt<NumInt, int>(10);  // 100
            var c = NegateIt<NumInt, int>(10);  // -10

            var t = DoubleIt<NumDouble, double>(10);  // 20
            var u = SquareIt<NumDouble, double>(10);  // 100
            var v = NegateIt<NumDouble, double>(10);  // -10

            var x = DoubleIt<NumBigInt, BigInteger>(10);  // 20
            var y = SquareIt<NumBigInt, BigInteger>(10);  // 100
            var z = NegateIt<NumBigInt, BigInteger>(10);  // -10
        }

        public static A DoubleIt<NumA, A>(A value) where NumA : struct, Num<A> =>
            default(NumA).Add(value, value);

        public static A SquareIt<NumA, A>(A value) where NumA : struct, Num<A> =>
            default(NumA).Multiply(value, value);

        public static A NegateIt<NumA, A>(A value) where NumA : struct, Num<A> =>
            default(NumA).Subtract(default(NumA).FromInteger(0), value);
    }
1 comments

That doesn't account for overflow, though...
Sorry, I don't follow?
The .NET implementation detects overflow and reports it back. This doesn't.
This:

    var x = DoubleIt<NumInt, int>(Int32.MaxValue);  // 20
Performs exactly the same as this:

    var a = Int32.MaxValue;
    var b = Int32.MaxValue;
    var c = a + b;
DoubleIt calls NumInt.Add which invokes the same code, why wouldn't it work in exactly the same way?