dotnet / csharplang

The official repo for the design of the C# programming language
11.49k stars 1.02k forks source link

Allow operators to be defined as instance methods. #2295

Open lostmsu opened 5 years ago

lostmsu commented 5 years ago

Yes, we've read https://blogs.msdn.microsoft.com/ericlippert/2007/05/14/why-are-overloaded-operators-always-static-in-c/

However, there are many scenarios, when having operators to be static methods fail. Main one being inability to represent generic algebras, which is hindering efforts to make C# suitable for scientific computing.

My proposal is simple: allow defining operators without static keyword, and permit them on interfaces. When performing operator overloading, always choose static operators defined in 7.0 way, if present, and issue a warning, if there is also a matching instance operator.

E.g.

interface IAlgebraic<T>{ T operator+(T other); }

struct Vector3<T>: IAlgebraic<Vector3<T>> where T : IAlgebraic<T>, new() {
  public T A,B,C;
  public Vector3<T> operator+(Vector3<T> other)
    => return new Vector3<T>(A + other.A, B + other.B, C + other.C);
}

P.S. IMHO, the fact, that users of the feature can shoot themselves in the feet by making asymmetric addition that way should not prevent legitimate use. There are users, who would be able to do that with inheritance, but nobody is arguing for it to be deprecated.

YairHalberstadt commented 5 years ago

However, there are many scenarios, when having operators to be static methods fail. Main one being inability to represent generic algebras, which is hindering efforts to make C# suitable for scientific computing.

Can you add this scenario to your proposal please?

lostmsu commented 5 years ago

@YairHalberstadt , sure, no problem. I just assumed, that everyone tried this hundreds of times, and know what I am talking about.

UPD. Moved the code sample into the initial post.

YairHalberstadt commented 5 years ago

Never assume 🙂

YairHalberstadt commented 5 years ago

There are various proposals around type classes that aim to address this problem from the opposite angle. Namely, allow interfaces to declare static members.

One disadvantage is they will only work in generic contexts.

lostmsu commented 5 years ago

@YairHalberstadt type classes require CLR changes, or type erasure. This needs neither.

YairHalberstadt commented 5 years ago

@lostmsu No they don't. See the shapes proposal.

lostmsu commented 5 years ago

@YairHalberstadt the shapes proposal seems to cover what I am looking for, but the implementation cost would be much higher, and would introduce generic parameter bloat. Since CLR has (or at least used to have) very tight restrictions on the number of generic parameters, it might not be viable.

YairHalberstadt commented 5 years ago

The shapes proposal was proposed by Mads Torgerson, the program manager for C#. They are likely going to look very very hard into implementing it.

It could be it isn't actually manageable, but I would hold on for now...

lostmsu commented 5 years ago

@YairHalberstadt well, I've done that for F# back in university ~10 years ago: https://archive.codeplex.com/?p=ntypeclasses From my experience, it is not elegant enough.

YairHalberstadt commented 5 years ago

Elegant, as in elegant to use, or elegant as in elegant CodeGen?

CyrusNajmabadi commented 5 years ago

Shapes/TypeClasses/etc. has a lot of interest at the LDM level. There are many reasons for this. However, one of hte primary ones is that it's felt that there are so many issues/proposals/things-people-want that wuold just fall under such an umbrella. So, instead of having to define 50 different features to do everythin in a one-off fashion, you just define the one large and flexible feature that subsumes all the rest at an acceptable-enough level.

lostmsu commented 5 years ago

@YairHalberstadt elegant as in design and semantics, and also codegen. New type declaration keywords, the need for implicit TypeClass type parameter, a whole new set of rules to find an appropriate implicit instance. For codegen, as I mentioned - generics bloat, which might require CLR upgrade.

lostmsu commented 5 years ago

@CyrusNajmabadi , as far as I can see, having type classes would not make the code in the example as elegant as it could be, because you'd still have to call T.Add(T other) instead of simply doing T + T unless what I request is implemented.

YairHalberstadt commented 5 years ago

You should be able to define operators in type classes, and by constraining a type parameter to a type class you would be able to write T + T as desired.

lostmsu commented 5 years ago

@YairHalberstadt , as under the hood the type classes are represented as interfaces, that would imply you can define operators on interfaces.

YairHalberstadt commented 5 years ago

I believe F# makes heavy use of generics, and it seems to run fine. Func and Action are both defined for up to 16 type parameters. I do not know for sure, but I think it likely the CLR will cope fine.

There are other potential performance issues, such as jitted code bloat, possibly leading to cache misses, but I think that it's important to run tests on large, real life codebases to get an idea if there are any performance issues in practice.

HaloFour commented 5 years ago

@lostmsu

They are interfaces under the hood, and all static members (including operators) would be instance members on that interface, but that is all implementation details. The developer would declare those members as static members on the "shape" type and they would call them like normal operators or static members. The compiler will handle converting that to instance member calls to the "shape" interface and the witness struct will handle proxying those calls to the actual static members and operators.

YairHalberstadt commented 5 years ago

as under the hood the type classes are represented as interfaces, that would imply you can define operators on interfaces.

Nope. You could define an unspeakable method for the witness struct, and use an attribute to indicate it should be used as an operator.

HaloFour commented 5 years ago

@lostmsu

I believe this is where the conversation (publicly) left off: #1711

The section under "Static interface members" explicitly discusses static members and operators.

All of this is up in the air, but anytime I've seen it discussed this is the general example, a type with a static property and static operators.

YairHalberstadt commented 5 years ago

Basically what I'm trying to say here is:

Shapes are almost definitely going to be looked at. They may be implemented, or they may not, but these type of features are unlikely to be considered till shapes are decided on.

It's still great to bring up these issues as they help guide the LDM as to what shapes should be capable of doing. I.e. shapes have to be powerful enough to solve these 30 issues.

If shapes don't end up happening, then all those issues might be looked at again.

lostmsu commented 5 years ago

@HaloFour I can perfectly understand how that would work.

Problem is this spec is extremely large, and proposal is not even finished (for example, it lacks exact rules to resolve default instances). I can hardly see it getting through all the necessary stages and approvals in the next 5+ years.

Whereas what I suggest here can be hacked in a day, and implemented and well-tested in 2-4 weeks. And would be immediately useful for the concepts proposal too.

CyrusNajmabadi commented 5 years ago

Whereas what I suggest here can be hacked in a day, and implemented and well-tested in 2-4 weeks. And would be immediately useful for the concepts proposal too.

@lostmsu As i mentioned, the LDM would like to avoid N 'smaller' one-off proposals like this.

HaloFour commented 5 years ago

@lostmsu

Problem is this spec is extremely large, and proposal is not even finished (for example, it lacks exact rules to resolve default instances). I can hardly see it getting through all the necessary stages and approvals in the next 5+ years.

And it solves a lot of problems, including your own. If history is any indication the team prefers to solve the big problems rather than trying to piecemeal a dozen disparate ways of doing things. This concept has the attention of the program manager of the C# team so it's likely to get a lot of attention after C# 8.0 ships.

Whereas what I suggest here can be hacked in a day, and implemented and well-tested in 2-4 weeks. And would be immediately useful for the concepts proposal too.

You could have the entire team jumping up and down about this specific proposal and it would never happen in the next 2-4 weeks, or the next 2-4 months, or likely within the next year. This repo doesn't move that way.

Yes, big projects take time. Many features slated for C# 8.0 were being discussed on CodePlex 3 years ago. The team is conservative and very deliberate. They're very unlikely to consider a small one-off solution that fits under a bigger umbrella unless it can be demonstrated that the bigger umbrella can't solve the problem or is itself not going to happen.

lostmsu commented 5 years ago

@CyrusNajmabadi I don't see why we can't have both. How many "one-off" proposals with no new concepts or keywords in them, that address parts of what shapes proposal is trying to get at?

lostmsu commented 5 years ago

Let's not run around in circles.

This concept has the attention of the program manager of the C# team so it's likely to get a lot of attention after C# 8.0 ships.

@MadsTorgersen

HaloFour commented 5 years ago

@lostmsu

New grammar, changes to the spec, development, testing, and support until the heat death of the Universe.

CyrusNajmabadi commented 5 years ago

@CyrusNajmabadi I don't see why we can't have both

Because it's redundant. Why solve the problem two ways instead of just solving it one way? It's a better use of resources, and it makes things less confusing and cheaper to maintain over the life of the language.

lostmsu commented 5 years ago

New grammar, changes to the spec, development, testing, and support until the heat death of the Universe.

Yeah, which I estimated for this feature to be 2-4 weeks tops for a single person. Do you disagree with that estimate?

HaloFour commented 5 years ago

@lostmsu

Yeah, which I estimated for this feature to be 2-4 weeks tops for a single person. Do you disagree with that estimate?

Yes, because this will involve the team, not a single person, and team is mindful as to the state of the language 5 years from now. They don't hack stuff together that quickly.

YairHalberstadt commented 5 years ago

@lostmsu Cyrus worked on the Roslyn team for many many years, was a member of the LDM, and is one of the most frequent contributors to Roslyn, despite no longer working at Microsoft.

If he says it'll take that long, you better believe it.

YairHalberstadt commented 5 years ago

Oh wait, that was HaloFour

lostmsu commented 5 years ago

@CyrusNajmabadi

Because it's redundant. Why solve the problem two ways instead of just solving it one way? It's a better use of resources, and it makes things less confusing and cheaper to maintain over the life of the language.

You are making a really huge assumption here, that shapes or type classes will ever be implemented in C#.

F#, which derived from ML, and had very hand-wavy design process, never got to implementing it in years even in an experimental setting. That is even though ML had it in the first place. And trust me, there was no lack of desire for it.

lostmsu commented 5 years ago

@HaloFour

5 years from now

5 years from now the market of scientific computing will be dominated by everything but C#, and there will be no way in.

YairHalberstadt commented 5 years ago

Not meaning in any way to say we should believe you less @HaloFour 😉

CyrusNajmabadi commented 5 years ago

You are making a really huge assumption here, that shapes or type classes will ever be implemented in C#.

And? The goal of the LDM is to assess and make reasonable decisions based on many potential futures. There is enough of beliefe that shapes/type-classes/etc. will be done to warrant not doing the 30 feature requests out there that fall under that umbrella. Even if it was only a few weeks worth for each (and it's not, it's sooo much higher), the LDM does not want to spend that to add 30 sub features, instead of one feature that would solve all of them (even if it takes longer to design and implement).

If, at some point, it becomes highly unlikely that shapes/etc. get done, then the views on these individual features will change.

I get that you don't like that's the current place where things land. But that's life. :-/

YairHalberstadt commented 5 years ago

The issue with 30 sub features is not just that all 30 have to be implemented.

It's that any new feature I add after that, has to consider how it interacts with all those 30 features. When I write a tests for a new feature, I've now got to consider 30 extra cases and decide whether they need a test.

And this is not theoretical. I literally right now am writing a test case to see how my new Roslyn API interacts with using declarations, which I would not have to do if they were not being added to the language.

lostmsu commented 5 years ago

The issue with delaying these "30 sub features" for me is that the shapes/type classes feature is not, apparently, time boxed. E.g. I'd be fine if LDM would say "we will not ship C# 9 until shapes is implemented", or "if shapes is not past proposal stage by some moment in C# 9 timeline, we will take a look at simpler alternatives".

Otherwise after C# 9 release this Go joke will become relevant for C#: Go generics

HaloFour commented 5 years ago

@lostmsu

A delayed feature is potentially good; a bad feature is bad forever.

CyrusNajmabadi commented 5 years ago

for me is that the shapes/type classes feature is not, apparently, time boxed.

Sorry. Things are not time boxed in the language.

E.g. I'd be fine if LDM would say "we will not ship C# 9 until shapes is implemented",

The LDM will not do that. It never has done that. Features come and go as things are learned. For example,if efforts start on shapes, and it turns out to be non-feasible, they will be scrapped. This has happened numerous times in the lifetime of C# and it will likely happen again.

or "if shapes is not past proposal stage by some moment in C# 9 timeline, we will take a look at simpler alternatives".

The team publishes what they are working toward. There is no guarantee either htat if shapes do not make it in 9 that these others will be worked on.

CyrusNajmabadi commented 5 years ago

Otherwise after C# 9 release this Go joke will become relevant for C#:

So what? No one working on C# cares if someone makes a joke about it because some feature is delayed. Responsible language development involved careful and thoughtful consideration about the future and where you want the language to be then. It means often not looking at cheap shortcut possibilities in the nearterm in spite of better approaches that can come later.

Note: these lessons have been hard learned by many language designers working on many different languages over decades. And, even knowing this stuff, we still screwed up some verssions of C# and are stuck with language features we wish we could get rid of.

Your feedback has been noted, and your desire to have this is well understood. But the language willl proceed at the pace it feels is appropriate, even if that means missing out on short-term wins in some arenas.

lostmsu commented 5 years ago

So what? No one working on C# cares if someone makes a joke about it because some feature is delayed.

They should. Developers are generally clever enough folks to start making jokes for a good reason.

HaloFour commented 5 years ago

They should. Developers are generally clever enough folks to start making jokes for a good reason.

This is not a constructive avenue for dialog. The team are very used to criticism regarding their decisions. If they were afraid of doing or not doing something based on whether or not someone would disagree the language would quite literally never have been designed in the first place.

CyrusNajmabadi commented 5 years ago

They should

No, they should not. Someone will always think your language is a joke. Driving to make that group of people happy is an inappropriate focus.

Let me make it totally clear: at every single step of C#, there have been people who consider it a joke to use the language (for any number of reasons). No one cares. The language isn't going to shift how it does development to make that group happy.

--

Note: bringing up 'Go' is rather strange in and of itself, given that 'go' is more loved than C# based on the most recent StackOverflow survey:

image

So, even i people are making fun of 'go' so what? Why is that at all something important to care about?

lostmsu commented 5 years ago

This is not a constructive avenue for dialog.

Neither is the whole talk about why this should be superseded by a feature, that has no apparent horizon.

CyrusNajmabadi commented 5 years ago

Neither is the whole talk about why this should be superseded by a feature, that has no apparent horizon.

Yes it is. You're asking for a feature in the short term. I'm explaining why that's not felt ot be appropriate. It's entirely on topic and appropriate. I'm not sure why you're rejecting this explanation. It's accurate and important to understnd.

At this point i'm not sure what else to say. We're at am impasse.

  1. You want feature X. I personally think your proposal is reasonable.
  2. Your feature request fits under the umbrella of a larger feature that aims to solve both your needs and the needs of many other users (and many other feature requests).
  3. The LDM considers that umbrella request important and is something they will likely look at.
  4. As such, your proposal will likely not happen as an independent feature.

I'm not sure what of the above is under contention. It's a factual statement about the current state of affairs. Nothing is likely to change about any of the above. If, in teh future, the LDM finds that the umbrella features are just unlikely to happen, your feature request may be reprioritized accordingly. Until there, there's little that will change here in the foreseeable future.

lostmsu commented 5 years ago

@CyrusNajmabadi IMHO, "Love" here is quite skewed. I am sure F# for the long time had more of it, than C#, being niche. Look at this one, depicting Go hype train, and its fall after the Jul-Aug 2018 announcement, that 2.0 won't have generics: https://www.tiobe.com/tiobe-index/go/

HaloFour commented 5 years ago

C# isn't a popularity contest. Proposals aren't considered on the basis that they'll increase the TiOBE index for the language.

lostmsu commented 5 years ago

@HaloFour what is the basis then?

HaloFour commented 5 years ago

@lostmsu

Whether the feature solves problems and fits within long-term vision of the language.

CyrusNajmabadi commented 5 years ago

IMHO, "Love" here is quite skewed.

As are "jokes". You're the one who opened up that part of the discussion. I'm just pointing out that that it's not an the objective of hte C# language to minimize people making jokes about it.