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

Add a special Numeric structure or INumeric Interface #25058

Closed
ghost opened this issue Feb 15, 2018 · 24 comments
Closed

Add a special Numeric structure or INumeric Interface #25058

ghost opened this issue Feb 15, 2018 · 24 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Numerics
Milestone

Comments

@ghost
Copy link

ghost commented Feb 15, 2018

Intrinsic numeric types (byte, int, float, ….etc) are structures, so unfortunately, they have no base class that can gather all basic methods and operators!
So I suggest to add a special Numeric structure that can accept numeric values of any type. It should contain the value and the underling type of it, as Object class does. Numeric structure should have the basic arithmetic and logical operators. Or it can be an interace (say INumeric) that all numeric data types implement. This will not break any existing code.

Usage:
Numeric structure (or INumeric Inrerface) can be used in generic methods where T is expected to be a numeric type, to carry out the basic arithmetic and logical operators without writing a dozen if statements for each individual numeric type. Objects doesn't have the basic arithmetic and logical operators and have to be converted to numeric types to work with. For example, I tried to do this but failed:

public struct Numeric
  {
      public object Value { get; set; }

      public static Numeric operator +(Numeric n1, Numeric n2)
      {
          return (Numeric)(n1.Value + n2.Value);
      }
  }

C# refuses n1.Value + n2.Value because it doesn't know how to add objects. Many if statements are needed to deal with different types of n1 and n2, regarding the larger type to use as the output type. This is a hell of code to something that should be easy and direct if there was a general numeric type that can contain any numeric type.

I face the same situation when I use the base Enum class in any method, because Enum can have any numeric type as an underling type!

I saw similar problem in the source code of Vector, where tons of code lines are repeated just to cover all cases of each numeric type!

Note: If you add the numeric type, C# and VB.net should add a new constraint for generic parameters to be numerics.
where T: numeric
this means T can only be of type Numeric or any intrinsic numeric data types.

So, If this Numeric Type or INumeric Interface exists, one can write

public static Enum SetFlag(Enum value, Enum flag)
{
var result = (Numeric)value | (Numeric)flag);
return (Enum)result; 
}

instead of:

public static Enum SetFlags(Enum value, Enum flag)
{
   if (Enum.GetUnderlyingType(value.GetType()) == typeof(byte))
      return (Enum)(object)((byte)(object)value | (byte)(object)flag);

   if (Enum.GetUnderlyingType(value.GetType()) == typeof(int))
      return (Enum)(object)((int)(object)value | (int)(object)flag); 
   //
   //
   // etc
}

and the same when writing generic types that deal with numeric data types only.
This can be extended based on this proposal dotnet/csharplang#1233, I also suggest to add these interfaces:
numeric constraint.
IIntegral: for integer tyoes (byte, sbyte, int, uint, short, ushort, long, ulong)
IFloating: (float, double, decemal)
IValue: (numerics and dates).
ILetiral (Char and string).
IPrimitive (numerics, dates, char, string)

These Interfaces will make writting generics easier. IPerimative can help you
format or parsing or using + operation depending on where T: IPerimative.
Also it can be usefull when defining generic lists and dictionaries that can contain different values. This will make them more specialized than using objects.

@jnm2
Copy link
Contributor

jnm2 commented Feb 16, 2018

I'm pretty sure dotnet/csharplang#164 is going to be the answer for this.

@ghost ghost changed the title Add a special Numeric structure Add a special Numeric structure or INumeric Interface Feb 16, 2018
@ghost
Copy link

ghost commented Feb 16, 2018

Could you explain how I do some thing like this with shapes?
public static Enum SetFlag(Enum value, Enum flag)
{
var result = (INumeric)value | (INumeric)flag);
return (Enum)result;
}

@yaakov-h
Copy link
Member

Wouldn't that be doable with just dotnet/csharplang#104?

public static TEnum SetFlag<TEnum>(TEnum value, TEnum flag) where TEnum : Enum, struct
    => value | flag;

@ghost
Copy link

ghost commented Feb 16, 2018

I asked for similar lang. feature here
dotnet/csharplang#1233
But i think making it in the framework will be a general solution for all languages.

@ghost
Copy link

ghost commented Feb 16, 2018

Based on my c# proposal, I also suggest to add these interfaces:
numeric constraint.
IIntegral: for integer tyoes (byte, sbyte, int, uint, short, ushort, long, ulong)
IFloating: (float, double, decemal)
IValue: (numerics and dates).
|Letiral (Char and string).
IPrimitive (numerics, dates, char, string)

@jnm2
Copy link
Contributor

jnm2 commented Feb 16, 2018

@MohammadHamdyGhanem

Could you explain how I do some thing like this with shapes?

@yaakov-h's suggestion is better, but off the top of my head you'd make the method take <T> where T : SEnum and define the | operator in an SEnum shape.

@jnm2
Copy link
Contributor

jnm2 commented Feb 16, 2018

IIntegral, IFloating, ILiteral, IPrimitive

As soon as you use any of these things as parameter or return types, you end up boxing all these tiny value types on the heap. Not good for performance.

Also, DateTime and decimal are not primitives as far as the type system is concerned.

All of that begs the question, what would you do with an instance of ILiteral or IPrimitive if you had one?

@ghost
Copy link

ghost commented Feb 16, 2018

These Interfaces will make writting generics easier. IPerimative can help you
format or parsing or using + operation depending on where T: IPerimative.
Also it can be usefull when defining generic lists and dictionaries that can contain different values. This will make them more specialized than using objects.

@jnm2
Copy link
Contributor

jnm2 commented Feb 16, 2018

But what can you do with where T : IPrimitive that you can't do without that constraint?

@ghost
Copy link

ghost commented Feb 16, 2018

In fact, I faced situations that need INumeric, and this is the most important. I proposed the other as a general solution "just in case".
The other constraints are in desdendent order regarding their importance.

@jnm2
Copy link
Contributor

jnm2 commented Feb 16, 2018

I proposed the other as a general solution "just in case".

"Just in case" proposals are often considered an antipattern.

@aaronfranke
Copy link

aaronfranke commented Feb 28, 2018

I am interested in numeric constraints so that I can create math classes that can use any numeric data type. For example, to make a Vector3 that can use float, double, or decimal. With only <T> and T x, y, z; I am unable to do things like z = x + y; or if (x > y)

dotnet/csharplang#1345

@eerhardt
Copy link
Member

eerhardt commented Mar 15, 2018

The original proposals of having a struct Numeric that wraps an object, or having an INumeric interface, are not feasible. This would cause boxing of every number, which will not perform satisfactorily.

However, the issue described here is an issue that we would like to fix some point. We have the same need in the Tensor<T> type in corefxlab: https://github.com/dotnet/corefxlab/blob/master/src/System.Numerics.Tensors/System/Numerics/TensorArithmetic.tt.

We would probably need a combination of new C# language features, and a built-in "Arthimetic" shape/type-class.

/cc @ericstj

@ghost
Copy link

ghost commented Mar 15, 2018

@eerhardt
So, all we need is a new keyword that works as arithmetic constrain (i.e. enum, arith or num)

class foo<T> where T : arith
{
   void foo1(T x, T y)
   {
        T z = x + y;
   }
}

The purpose of the arith keyword is to tell the compiler at design time that x + y is a valid operation, and to ensure that T must be a numeric data type. At run time, T is known and x + y should execute as normal.

@ghost
Copy link

ghost commented Mar 15, 2018

It came to me now, that we also need to constrain object and dynamic types. maybe something like this:

object x = enum1 where x: arith ;
object y = enum2 where y: arith;
var z = x + y;

Or it can be shortened like this:
var z = (arith)enum1 + (arith)enum2;

But I think Enum class should be some how marked as arith, so there will be no need for any more casting:
var z = enum1 + enum2;

@aaronfranke
Copy link

@MohammadHamdyGhanem I think it would make more sense to call it number instead of arith

@ghost
Copy link

ghost commented Mar 16, 2018

@aaronfranke
Any suitable name is OK.

@khellang
Copy link
Member

@ghost
Copy link

ghost commented Mar 16, 2018

I looked at the Int32 structure here:
https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/int32.cs
There is a commented out code that implements the IArithmeticinterface!>> I sarches for the reason and I fond this:
https://stackoverflow.com/questions/41832780/iarithmetict-interface-in-c-sharp
Clearly there was trilas to solve the numeric types problem, but it is not successfull yet!

@ghost
Copy link

ghost commented Mar 16, 2018

@jnm2 , @yaakov-h , @aaronfranke , @eerhardt , @khellang
I posted a new proposal with a new approach:
dotnet/csharplang#1387, Proposal: define signatures for generic type parameters

@ericstj
Copy link
Member

ericstj commented Mar 16, 2018

@MohammadHamdyGhanem indeed, folks care about this problem very much. It's no coincidence that multiple language proposals show how they intend to help solve this problem. I wasn't around for the initial IArithmetic experiments but my understanding is that an interface and instance methods had both poor performance and bad semantics (because they cannot use operators).

For operators you need static interfaces, static interfaces could also solve the performance problem since the JIT could assume an impl based on the T and specialize to optimize away the method call without actually having to box the primitive type. This was alluded to in @MattWindsor91's writeup: https://github.com/MattWindsor91/roslyn/blob/master/concepts/docs/csconcepts.md#static-interface-methods-for-the-clr

As mentioned static interfaces require changes in both runtime and frameworks so its less than ideal for reach.

I think the current C# proposal for type classes/shapes has much more promise. It has the potential to work with purely compiler features, though we might add some default implementations of the shapes/concepts/whatever-they-get-named in the framework itself to facilitate ease of use and type exchange.

I'd highly recommend you look through the type class / shapes issues in CSharpLang. I believe those are good generalizations of this type of problem and have the best chance at getting a solution that can reach to the largest number of C# developers.

@ghost
Copy link

ghost commented Mar 16, 2018

@ericstj
Thanks for the details.
I proposed to define a segnature dotnet/csharplang#1387dotnet/csharplang#1387, Proposal: define signatures for generic type parameters. Maybe it is like the contract, but my aim is for generics, and no need to change the current CoreFX classes and structs to deal with.

@ghost
Copy link

ghost commented Mar 2, 2019

@yaakov-h
I tried the Enum constraint but it refuses to use int as a type param, saying int can't be converted to Enum. So, unfortunately, Enum is not for basic numeric types!

@tannergooding
Copy link
Member

tannergooding commented Apr 22, 2021

Closing this. As mentioned in other issues (such as #50647) we are looking into the correct surface area to expose here as part of the static abstracts in interfaces feature the language and runtime are adding.

This is currently being tracked in our dotnet/designs repo and I've added the initial rough draft of the surface area here: dotnet/designs#205

Further discussion or suggestions should happen there and in future PRs that further refine the proposed surface area.

@ghost ghost locked as resolved and limited conversation to collaborators May 23, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Numerics
Projects
None yet
Development

No branches or pull requests

9 participants