Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Proposal] Add interface hierarchy to primitive types #3423

Closed
ja72 opened this issue May 4, 2020 · 12 comments
Closed

[Proposal] Add interface hierarchy to primitive types #3423

ja72 opened this issue May 4, 2020 · 12 comments

Comments

@ja72
Copy link

ja72 commented May 4, 2020

The intent is to help with generic constrains, extension methods, and generally handling primitives types.

// Root
public interface IPrimitive : IEquatable { } 

// Numeric types
public interface INumeric : IPrimitive, IComparable { }
public interface IInteger : INumeric { }
public interface IBool : IInteger { }       // bool

// Integer types (signed)
public interface ISigned : IInteger { }
public interface ISByte : ISigned { }       //sbyte
public interface IInt16 : ISigned { }       //short
public interface IInt32 : ISigned { }       //int
public interface IInt64 : ISigned { }       //long

// Integer types (unsigned)
public interface IUnsigned : IInteger { }
public interface IByte : IUnsigned { }      // byte
public interface IUint16 : IUnsigned { }    // ushort
public interface IUint32 : IUnsigned { }    // uint
public interface IUint64 : IUnsigned { }    // ulong

// Floating point types
public interface IFloat : INumeric { }  // floating point numbers
public interface ISingle : IFloat { }       // 32-bit `float`
public interface IDouble : IFloat { }       // 64-bit `double`

// Fixed point types
public interface IDecimal : INumeric { }    // decimal

// Other
public interface IChar : IUint16 { }     // char

The specialized libraries can hook into this to defined other custom types that will play well within the existing framework

public interface IComplex: IFloat { }       
public interface IBigInteger : IInteger { }
public interface IRational : INumeric { }       
public interface ISymbol : INumeric { }
public interface IHalfFloat : IFloat { }

Additionally, it would make sense that arithmetic operators and comparisons be required to be implemented to create concrete types from the INumeric interface.

The addition of the above interfaces and implementation with primitive types would make the CLR far more flexible and extensible for scientific computing applications. It would allow for more generic code writing such as:

public struct Vector3<T> where T: INumeric
{
    public T X { get; }
    public T Y { get; }
    public T Z { get; }

    public Vector3(T x, T y, T z)
    {
        this.X = X;
        this.Y = Y;
        this.Z = Z;
    }
    public static Vector3<T> operator + (Vector3<T> a, Vector3<T> b) 
        => new Vector3<T>(a.X + b.X, a.Y + b.Y, a.Z + b.Z);

    public static Vector3<T> operator * (IReal f, Vector3<T> a)
        => new Vector3<T>(f * a.X, f * a.Y, f * a.Z);
}

and the compiler would know how to bind the arithmetic operators or do the implicit conversions (like from float to double) to make the code work without having to write different versions for different numeric types.

@HaloFour
Copy link
Contributor

HaloFour commented May 4, 2020

Wrapping all of the primitive types with interface implementations would be a CLR concern, you could request that here: https://github.com/dotnet/runtime/issues

However, doing so would have some big disadvantages. Calling a method through an interface like this requires boxing the value type which incurs an allocation on the heap, plus the virtual dispatch of calling the instance member on the interface. Generic specialization on structs may help to avoid some of this.

The C# team is proposing solving these kinds of problems in a different way: #1711
The part that's relevant is under the heading "An example: Numeric abstraction" that defines a numeric monoid.

@ja72
Copy link
Author

ja72 commented May 5, 2020

The C# team is proposing solving these kinds of problems in a different way: #1711
The part that's relevant is under the heading "An example: Numeric abstraction" that defines a numeric monoid.

Well, at least the team recognize the shortcoming and are working on at. Thanks for the issue# link. I heard of monoids before and had no idea what they were talking about, but I am beginning to understand.

@john-h-k
Copy link

john-h-k commented May 5, 2020

// Floating point types
public interface IReal : INumeric { }
public interface IFloat : IReal { } // float
public interface IDouble : IReal { } // double

Nit: should be ISingle as that is the proper name. But also, why would there be IDouble? I get IReal, but surely IDouble will only be implemented by Double

@ja72
Copy link
Author

ja72 commented May 5, 2020

// Floating point types
public interface IReal : INumeric { }
public interface IFloat : IReal { } // float
public interface IDouble : IReal { } // double

Nit: should be ISingle as that is the proper name. But also, why would there be IDouble? I get IReal, but surely IDouble will only be implemented by Double

IReal is there to distinguish between scalar values and non-scalar such as IComplex or IDual.

Then under IReal you might have ISingle for 32-bit floats, IDouble for 64-bit floats and maybe IExtended for 80-bit floats. You are correct that IFloat might be confusing.

@tannergooding
Copy link
Member

IReal also doesn't correctly capture things like NaN. Additionally, depending on who you ask PositiveInfinity and NegativeInfinity may or may not quantify as "real numbers".

@ja72
Copy link
Author

ja72 commented May 6, 2020

IReal also doesn't correctly capture things like NaN. Additionally, depending on who you ask PositiveInfinity and NegativeInfinity may or may not quantify as "real numbers".

A NaN is an artifact of a concrete type such as float or double, so it should be captured under the corresponding types, and it has nothing to do with contracts the interfaces present.

@tannergooding
Copy link
Member

It is not just an artifact, it is a fundamental value of the float and double type and therefore something that could be exposed via any IReal interface that was created from a float, double, or from performing certain operations on them.

It is something that would need to be understood and rationalized with these interfaces if they were ever to become a real thing.

@gerhard17
Copy link

gerhard17 commented May 6, 2020

I like the idea that primitive types implement a set of standardized interfaces this could be useful in generic constraints..

...but public interface IComplex: IReal { } (for example) is mathematically wrong.
Not every complex value is a real value. (only those with imaginary = 0)
Instead the reverse is true: every real value is also a complex one!

Difficult to known in advance, which interface hierachy is needed.

@ja72
Copy link
Author

ja72 commented May 6, 2020

I like the idea that primitive types implement a set of standardized interfaces this could be useful in generic constraints..

...but public interface IComplex: IReal { } (for example) is mathematically wrong.
Not every complex value is a real value. (only those with imaginary = 0)
Instead the reverse is true: every real value is also a complex one!

Difficult to known in advance, which interface hierarchy is needed.

You are right in that IReal made it seem like a real number, so I renamed it to IFloat since it represents the common ancestor for all floating-point numbers. In that sense, you represent a complex number as two floating-point numbers, and hence it still has the behavior of floating-point numbers and hence inherits from IFloat.

@ja72
Copy link
Author

ja72 commented May 6, 2020

Wrapping all of the primitive types with interface implementations would be a CLR concern, you could request that here: https://github.com/dotnet/runtime/issues

Is there a way to migrate the issue, or shall I close and re-post in the CLR hub?

@jnm2
Copy link
Contributor

jnm2 commented May 6, 2020

@ja72 You might want to read dotnet/runtime#14723 first.

See also dotnet/runtime#25058 and dotnet/runtime#18244.

@YairHalberstadt
Copy link
Contributor

Clsoing as duplicate of dotnet/runtime#14723 and other issues on dotnet/runtime

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants