Discussion: change method signatures to include return types #1587
Replies: 23 comments
-
Just thinking out loud here but this can create a mess, when you make a change to one of the overloads or introduce new ones it can actually break your code with no guarantees from the compiler but just as important if not more it means that now any overload that you add makes your code more entangled. |
Beta Was this translation helpful? Give feedback.
-
@eyalsk You can say the same for any type of overloading. If you call a method with some parameter and then introduce a better overload, that will silently take over. Or, if you have two compatible overloads and for some reason delete the better matching one, you'll end up calling the other. The compiler won't say a word in either case. extern void f(int i);
extern void f(short s); // Comment this out and the other one gets called.
void Main()
{
short s = 42;
f(s);
} |
Beta Was this translation helpful? Give feedback.
-
@philosaurus You're right but I think that having the same problem on both sides can make it worse, don't have any concrete example atm but I just know that looking at one direction is better than looking at both sides so might simplify type checking but could be wrong. ;) |
Beta Was this translation helpful? Give feedback.
-
One usecase: // existing API, can't remove for compatibility
Task<T> SomeMethodAsync();
// To improve performance
ValueTask<T> SomeMethodAsync(); related to #964 |
Beta Was this translation helpful? Give feedback.
-
I dare say that if this is something that the language team was interested in, it probably would have been done in .NET 4.5 to avoid the whole "Async" suffix mess. |
Beta Was this translation helpful? Give feedback.
-
Could have been nice, question is why they haven't done it? curious. I mean, I have a gut feeling that it would complicate things both at the compiler level and tooling but again maybe I'm wrong. |
Beta Was this translation helpful? Give feedback.
-
I think that even if the compiler could resolve the correct overload in some cases that the user experience would be a mess. Given a call to a method overloaded only on return type the compiler couldn't effectively determine a result type and thus Intellisense wouldn't be able to offer any real assistance. Overloading is already a complicated mess. We really don't need anything to make it more complicated. |
Beta Was this translation helpful? Give feedback.
-
How would the compiler decide which overload is called in code like |
Beta Was this translation helpful? Give feedback.
-
@svick |
Beta Was this translation helpful? Give feedback.
-
@HaloFour Let's assume you're right and user experience would indeed be a mess. (I'm not 100% sure about it, but I guess we'd need at least some experimental implementation to see.) How about return type overloading with the constraint that the return type is never inferred; one has to specify it explicitly? Say, you'd have to write Even this would be a good starting step, which could be relaxed later. |
Beta Was this translation helpful? Give feedback.
-
@philosaurus At that point, how is it better compared with |
Beta Was this translation helpful? Give feedback.
-
For multiple reasons:
|
Beta Was this translation helpful? Give feedback.
-
We're so beyond this point, aren't we? I mean, Async suffixes are all over the place and there are actually more examples like Task, As/To prefixes and whatnot. It might make it easier to name stuff but it will probably make other things hard to not worth the investment and because the framework is already loaded with prefixes and affixes that solves the problem to an extent it wouldn't make sense for them to use this feature now.
There are few proposals that try to address that.
How many of them exists in the wild? |
Beta Was this translation helpful? Give feedback.
-
Until the next round of Big Idea™ comes around. Besides, having a bad workaround to a problem does not make the problem nonexistent.
Great, but generic parameters are only inferred from their input types, not their output. (I don't think that's about to change with specialization.) Besides, using generic specialization to specialize an otherwise non-generic method just to have it work for multiple output types under the same name is a simple abuse of a feature.
Since I believe no mainstream .NET languages support return type overloading (possibly because the others don't support it either), not a lot, I believe. And until C# starts supporting it, not many will (at most some fringe language(s)). |
Beta Was this translation helpful? Give feedback.
-
I think that every feature added to the language should stand on its own. If the larger feature is actually desirable, I think it could be made all at once.
This goes the other way around too. Since C# is the main language, other languages pretty much have to be able to consume any library produced by C#. So adding this feature to C# would force other CLR languages to be able to consume libraries that use return type overloading. |
Beta Was this translation helpful? Give feedback.
-
Not sure how I feel about trying to get the language changed to prevent a codesmell from being a codesmell 🤔 I have wanted to overload the return type quite a few times, but there is just so many cases were the compiler has literally nothing to work off of. int Foo() => 42;
string Foo() => "42";
private void Bar(int i) {}
private void Bar(string s) {}
private void Baz<T>(T t) {}
...
Foo();
var x = Foo();
Bar(Foo());
Baz(Foo()); This isn't a, "maybe one day we'll find a way to do this," this is a, literally-impossible-without-being-able-to-read-minds. Any solution for this will look pretty much like public static void Main()
{
var i = Foo<int>(); // i = 42
var s = Foo<string>(); // s = "anything but 42"
int i = Foo(); // Error: Type arguments cannot be inferred (😣)
}
public static T Foo<T>()
{
if (typeof(T) == typeof(string)) return (T)(object)FooString();
if (typeof(T) == typeof(int)) return (T)(object)FooInt();
else throw new ArgumentException();
string FooString() => "anything but 42";
int FooInt() => 42;
} Local functions hide the undesirable names from everyone else and the final syntax is only a character longer than your proposed solution. Downsides are that generics don't infer backwards and it's unlikely they ever will because that causes issues too, casting and (un)boxing, being able to throw in any type, and having to check all the |
Beta Was this translation helpful? Give feedback.
-
From this StackOverflow question for why they don't have this yet in C#:
|
Beta Was this translation helpful? Give feedback.
-
@AustinBryan: The linked Software Engineering (not SO) post lists 5 points, you cherry-picked and quoted here only 2 of them. From this, a language designer can conclude: Your other points have already been answered above. |
Beta Was this translation helpful? Give feedback.
-
@philosaurus Of course I did because those were not serious answers. How about you try explaining what they should do in those for instances where there is literally no way to know what the programmer was intended for each overload? |
Beta Was this translation helpful? Give feedback.
-
@AustinBryan What exactly do you mean by "those were not serious answers"? 😄 |
Beta Was this translation helpful? Give feedback.
-
I meant not serious points.
Clearly he was kidding 😆 |
Beta Was this translation helpful? Give feedback.
-
@AustinBryan Oh okay, I thought you're referring to the comments that made in this post. 😆 |
Beta Was this translation helpful? Give feedback.
-
How about you try explaining what they should do in those for instances where there is literally no way to know what the programmer was intended for each overload? I've literally answered this in the first point of the opening post: make it an ambiguity error. I'm not talking about solving the cases that cannot be decided by any computational means, I'm talking about those that can. I would even go ahead and say the set of programs still remaining ambiguous with this feature is a strict subset of those that are ambiguous without it (as the compiler not only freaks out on actually undecidable usages now, it doesn't allow any declarations that could potentially cause ambiguous usages). Here's a proof-of-concept pattern you can experiment with: __OverloadedReturnType fun(P1 p1, P2 p2, ...) { return new __OverloadedReturnType(p1, p2, ...); } struct __OverloadedReturnType { P1 p1; P2 p2; ... public __OverloadedReturnType(P1 p1, P2 p2, ...) { this.p1 = p1; this.p2 = p2; ... } public static implicit operator T1(__OverloadedReturnType @this) { // fun's original code goes here } public static implicit operator T2(__OverloadedReturnType @this) { // fun's original code goes here } ... } While this pattern is not perfect (you can't use Of course I did because those were not serious answers. While of course it's impossible to guess intentions of the original poster from here, I do feel lots of good ideas around here are refuted exactly on one of those grounds. Might be just my opinion. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I have done a best-effort search through this repo, but haven't found an issue about this, so here it is.
C# does not(*) allow overloading based on return type, so having two methods that have otherwise the same signature is disallowed. To me, this looks like having been inherited from C/C++. It also seems to me that more and more features are coming, or are at least being discussed, where types are inferred. Lambdas are the the prime example of that, but the new-ish syntax for
default
where you don't have to mention the type or the proposed type-inferrednew
(List<int> l = new ();
) is pretty similar, too.(*): It is not even that C# does not have return type overloading (neither is it true for C++, btw). A special case of this, namely implicit conversion operators, are supported, and they have nothing else to differentiate between them besides their return type. In fact, it is possible right now to implement something similar to return type overloading by returning a custom type that's implicitly convertible to whatever type it ends up being.
This would be an ambiguity error. Maybe if one of the overloads were to return
void
, it could be picked when the return value is not used.Could be either an ambiguity error or infer all the way through.
Using a typecast-like syntax
(string) x()
or evenstring x()
could disambiguate. I'm not sure if the latter would cause any syntax ambiguities.As C# does not currently allow two methods with matching signatures, I don't think any backwards compatibility is broken.
The CLR supports this at the very moment and no changes seem to be necessary.
Beta Was this translation helpful? Give feedback.
All reactions