-
Notifications
You must be signed in to change notification settings - Fork 1
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
Generic Code for Math Operations #1
Comments
As well as e.g. implementing public interface IEquatable<T>
{
bool Equals(T other);
// Implement IEquatable<TSelf>.Equals and get operators == and != for free.
virtual static bool operator ==(T left, T right) => left.Equals(right);
virtual static bool operator !=(T left, T right) => !(left == right);
} Subtraction can be defaulted to negation and add interface INumeric<TSelf> where TSelf : INumeric<TSelf>
{
abstract static TSelf Zero { get; }
abstract static TSelf operator +(TSelf a, TSelf b);
// Implement negation operator.
abstract static TSelf operator -(TSelf a);
// Default subtraction operator is implemented as negation and add.
virtual static TSelf operator -(TSelf a, TSelf b) => a + -b;
} This helps reduce the ceremony boilerplate where one operator can be defined in terms of another; while allowing the type to provide a more efficient (or different e.g. Quaternion) implementation if required. |
@benaadams I've given a little thought to |
@benaadams as for the shapes of the interfaces themselves, I was strictly following what Swift Numerics has done so far. |
Generic Code for Math Operations
Today C# does not allow code to be generic over math operations, so there is no way of expressing in generic code things like computing the average of a numeric type. Ideally, we would like to be able to write a generic function
Average
that can work on any numeric data type:It would be desirable to express that algorithm and have this work for
double
,float
,int
,decimal
and even newer data types like half-floats, or SIMD-accelerated types.In the example above, there are three capabilities missing: the initialization of the sum to a zero, the addition of numbers of the given type and the division of the number. And of course, there is no such thing as the
IReal
interface.F# has a ways of achieving the above solution, and we can draw some inspiration from this, to bring it to C#, and also draw inspiration from the work being done for Swift Numerics.
To address this problem, we want to introduce support to interfaces to declare static members that are a part of the interface contract, requiring that types that implement the interface should expose those as static members. While the word
static
would have been ideal for this, recently the wordstatic
was used in interfaces to declare helper methods. We will differentiatestatic
helper members fromstatic
contract members using theabstract
modifier.For the required elements above, this would allow us to author some interfaces, like this:
We would then make our existing data types in .NET conform to this new set of interfaces:
Behind the scenes, the JIT compiler would need to recognize the calls to those operators and replace those with the equivalent JIT operation. For example, a simple generic Add method:
Would produce IL that looks like this:
When the runtime finds this code for the built-in data types, it would treat that call to the
op_Addition
operator the same way that it would have treated theadd
opcode in that scenario. For other types, it would call the method.Additionally, it would be desirable to write generic code for elementary functions, which means
that we would like the types to implement those functions (
Sin
,Cos
,Tan
,Exp
,Atan2
and so on).This would let us express code like this:
Where the
IReal
interface would be:An alternative is to not require static methods in the classes, but rather surface these as instance methods, so rather than surfacing
Double.Sin (0.23)
developers could write(0.23).Sin()
.In either case, our core types would surface those methods. This means that
Double
would now have a methodSin
for example in addition to the existingSystem.Math.Sin
method that is already present.With this capability, new data types can be introduced, and they can independently choose to conform to the new numeric interfaces, allowing those types to be plugged into existing codebases.
Conversion Operators
Just like it is possible to define operators in interfaces, we would allow conversion operators to be declared on those interfaces. This would allow for implicit conversions in the code.
At the beginning of this section, I used
T.Zero
in generic code to represent the zero value. With this solution, we would not have to use this constant of limited value, but we could use any constants that we desire, or initialize from other values.This would allow code like this to be written
We would introduce a family of interfaces for common data types, and our core types would need to implement these:
Summary of Generic Code for Math Operations
So in short, we would need:
IAdditiveNumeric
,INumeric
,IReal
,ISignedNumeric
, new work is happening in Swift Numerics, and includes things likeIElementaryFunctions
).The text was updated successfully, but these errors were encountered: