-
Notifications
You must be signed in to change notification settings - Fork 1k
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 operators support in C#. #813
Comments
Related to this is the generic cast. This is currently not possible in C#, but feels like it should be: public sealed class NullObject
{
private NullObject() {}
public static readonly NullObject Instance = new NullObject();
public static implicit operator List<T>(NullObject o) => new List<T>();
} Sorry for the dumbness of this example. It's all I could think of right now. |
I'm entirely behind this, I've run into plenty of scenarios where this is necessary to reduce bloated and unclear code. |
Just wanted to bring this up, as shapes and extensions have nothing to do with this feature request, as it talks neither about extending an existing type (extensions), nor about generalizing over static members, which the underlying type system does not support (shapes). It also seems to work fine in F#, both declaration and consumption, without any special IL generation. |
The C# LDM considers it an important feature of the language that every place the language infers a type, you can also explicitly give one. This proposal does not include any way to explicitly provide the type argument at the invocation site. If this proposal were extended to have a way to do that, we don't believe we would like it. |
@gafter for the explicit type argument would it be possible to simply allow invoking the method via |
Update: Mixed things up badly. Just disregard.
At least we'd have a way to abstract over numerical operations (by having an
|
The proposal was put in the Likely Never milestone, and moved to the Rejected column in LDM triage. It's meant as strongly as it reads. |
"Simply"? Um, That would require a breaking change to name lookup. See |
@gafter I don't understand your example. Can you explain what would be broken? Here I am declaring My understanding from the generated IL is that unless And if you are talking about the case when the new generic operator will hide |
If invoking something called |
Oh, you are actually referring to this case: https://sharplab.io/#v2:C4LglgNgNAJiDUAfAAgJgAQEECwAoA3nusesgMykCMAbFugPYAOA+pjDGMGPQHYAUmdAENKUOkNQBKdAF4AfMMoBuPAF88eNOgBCILHkK4SpCsho6GjAKYAnIcHo34fbYrGuJ0+YpW51uTQwzAHYDIhI0VHDiQ2NjVwAjWXQeAFcICF84km0AOiZWdk5ufgSxBMkskn9/IA= where treating |
Maybe C++ style |
I just want to mention that generic operators in combination with foreach (int i in -6 -to- -4)
{
Console.WriteLine(i);
}
foreach (long l in -2 -to- 2)
{
Console.WriteLine(l);
}
foreach (char c in 'a'-to-'e')
{
Console.WriteLine(c);
}
string[] names = { "Sally", "Bob", "Joe", "Eric", "Patty" };
foreach (string s in names.Where("Allen"-tᴏ-"Kevin"))
{
Console.WriteLine(s);
} Source code: https://gist.github.com/ZacharyPatten/3b7d7d63bc5188d3f7c863639f14765d With generic operators the Be careful what you wish for. :P Another example could be measurement unit syntax. Stuff like:
|
The new |
@tannergooding from my understanding it does not. That feature is completely orthogonal to this feature request. The new operator interfaces do not cover the scenario in the head post where non-generic class class Class { operator+<T>(Class a, T b) => ... } The proposed interfaces simply do not work in this case, cause you would have to put specific type in place of class Class: IAddition<Class /* TSelf */, T /* TOther */, SomeResultType>
{
...
} But the problem is that |
@lostmsu what's the use case such an operator would be solving? |
@CyrusNajmabadi frankly, I don't remember by now what use case I originally had for this feature. A simple example I can come up with is implementing lisp-like tuples with var sfTuple = new Tuple<string, float>{ Head = "hi", Tail = 0xDAD };
var isfTple = 42 + sfTuple;
struct Tuple<THead, TTail> {
THead Head;
TTail Tail;
public static Tuple<T, Tuple<THead, TTail>>
operator +<T>(T value, Tuple<THead, TTail> tuple)
=> new () { Head = value, Tail = tuple };
} But in general any construction of objects with different generic parameters than input. The reason I dug up this issue this time was an attempt to implement the pipe trick: using System;
using static Pipe;
int f(int v) => v + 10;
int v = pipe(10)
.pipe(x => x * 2)
.pipe(f);
public static class Pipe {
public static Pipe<T> pipe<T>(T value) => new Pipe<T>(value);
}
public struct Pipe<T> {
public T Value{get;}
public Pipe(T value) => Value = value;
public static implicit operator T(Pipe<T> pipe) => pipe.Value;
// the DSL would be much better
// if the method below could be replaced with `operator |<TResult>`
public Pipe<TResult> pipe<TResult>(Func<T, TResult> op) => new (op(Value));
} |
Hi, there is a simple workaround using I use this technique extensively in many variations of it in my project RationalNumerics. It saves a ton of code - unfortunately for NET 7 we have to write all conversions explicitly to comply with the new coding rules. That relativates all the good new options into it's opposite. What nonsense. Good examples for the workaround are Float80, Float96, Float128 that all based on a template Float<T> but also more efficient performant types e.g a new BigInteger is not based on templates but uses them. The technique with the Simple code snippet for the approach: struct MyTempl<T>
{
T data;
public static implicit operator MyTempl<T>(T value) => new MyTempl<T> { data = value };
public static implicit operator MyStruct(MyTempl<T> value) => value.data is int x ? x : default; // this for template
}
struct MyStruct
{
int data;
public static implicit operator MyStruct(int value) => new MyStruct { data = value };
public static implicit operator MyStruct((int x, int y) value) => new MyStruct { data = value.x }; // this for tuple
public static bool operator <(MyStruct a, MyStruct b) => a.data < b.data;
public static bool operator >(MyStruct a, MyStruct b) => a.data > b.data;
}
static void test_MyStruct()
{
MyStruct a = 1, b = 2;
if (a < b) { }
if (b > (1, 2)) { }
if (b < (1, 2)) { }
MyTempl<int> c = 3;
if (b < c) { }
} |
In my case I want a simple implicit conversion from class MyClass<T>
{
public static implicit operator<U> MyClass<T>(MyClass<U> x) { ... }
}
using MyClass<int> Foo;
using MyClass<uint> Bar;
Foo x = new Foo();
Bar y = x; @c-ohle This won't work if I also want generic operators. I wonder why this isn't possible as it is for methods. The new static abstract interfaces don't change anything about this. |
Just because I haven't seen it mentioned, this feature would allow matrix multiplication with named axes:
|
Dear all, Matrix<double> A, B;
Vector<double> a, b, c, x;
double alpha, beta, gamma;
x = gamma*c + alpha*A*b - beta*B*b; Expression generics with generic operators will allow the syntax above avoiding operator overloading which results in temporary object (that consume a lot of memory for large array sizes > 10^7). C# will open the door for a huge number of scientists and engineers using Fortran at the moment or C++ because such clean and clear syntax is only supported since many decades in Fortran and a few years ago in C++ with expression templates. Furthermore,
these researchers currently use Fortran or C++ or languages that allow syntax closer to the mathematical expressions (Matlab, Julia), due to the complexity of the codes and formulas involved. Why not encouraging them to join our community and benefit from all this great machinery C# provides (extremely fast compilation, ease of coding, static analysis, memory safety, ...)? I would even go a step further and recommend that C# by design adopts natively such operations on its arrays double[], and matrices double[,]. Just so people who work in the areas above have nothing to lose switching to C#. |
+1 for this feature. Generic Math is currently incomplete without a generic operator I am expecting operator should be generic like => where T: GenericOperator |
+1 |
I wouldn't agree it is incomplete nor that having generic operators is a "good design" for many cases. Not only is there considerable nuance lost when talking about things like casting operators between arbitrary generic types; but there is a high amount of risk in terms of bugs and untracked behavioral issues when talking about cases like A lot of this boils down to what is API design that goes against the Framework Design Guidelines. Therefore being things where even if they existed, it is highly unlikely that the built-in interfaces that define the core of "generic math" for .NET would ever utilize them. Of course external libraries are free to define their own types and considerations, but the general integration with the rest of .NET is likely to suffer. For conversions, you have to consider things like:
Then for other operators, you have to start considering things like whether an implicit conversion from Not all convenient things are good. There was a deep consideration of many of these concepts and whether or not they were fundamentally "required" when the That doesn't strictly mean that adding generic operators shouldn't happen either, just that features like generic math are likely not the golden ticket to making that happen as they aren't a representative use case for the majority of users working with the feature or types implementing the feature. |
@dmitriyse commented on Fri Dec 18 2015
Currently c# does not support generic operators definition like that:
This support can be usefull to build advanced libs with expressions (validation expressions in my case).
This is only C# limitation, CLR ready for this feature.
We can already define operator in different syntax.
No changes in CLR needed to define generic operator in this syntax:
but later C# does not recognize it.
Please add this proposal to the C# 7.0 wish list.
@tpetrina commented on Fri Dec 18 2015
I would love this feature. Non-generic operators are limiting.
@dsaf commented on Sun Dec 20 2015
Related to #3391 and #2147.
@msedi commented on Sat Mar 19 2016
Yes. In my opinion this is now absolutely necessary. Having only generics that are not fully equivalent to C++ generics (in functional behaviour) are absolutely mandatory. I guess there are many guys out there requesting this feature. Instead a lot of people here are complaining about syntactic sugar.
@orthoxerox commented on Tue Apr 05 2016
Might help solve the problem of verbose generic patterns discussed in #10153. If
is
was generic the pattern could infer its generic type.@grwGeo commented on Thu Jul 21 2016
I have needed this feature trying to model mathematical entities and it is a definite must. It will save tons of repetitive code and make the conceptual model richer. Also type inference would do wonders in this scenario.
The text was updated successfully, but these errors were encountered: