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

Infer types from constraints #7850

Conversation

HellBrick
Copy link

This is a prototype of #5023 rebased on top of the future branch. See #5023 (comment) for the description of the implementation mechanics and #6644 for the original PR against master.

/cc @gafter

@davkean davkean added the Community The pull request was submitted by a contributor who is not a Microsoft employee. label Jan 8, 2016
@davkean
Copy link
Member

davkean commented Jan 8, 2016

Closing and reopening, so that merge branch gets updated.

@davkean
Copy link
Member

davkean commented Jan 8, 2016

@dotnet-bot retest this please
@dotnet-bot test eta please

@davkean
Copy link
Member

davkean commented Jan 8, 2016

What's the plans with this PR? You mention this is a prototype, are you looking for feedback? Or is this just to get some coverage for tests?

@amcasey
Copy link
Member

amcasey commented Jan 8, 2016

The test failures appear to due to some issues that have already been resolved in master. They should clear up as soon as we've merged master into future.

@davkean
Copy link
Member

davkean commented Jan 8, 2016

Ignore these failures for the moment, looks like future is broken.

@HellBrick
Copy link
Author

To be honest, I have no clue about what comes next for this PR. This is my shot at an up-for-grabs issue that I care about: I just came up with an idea on how to do it and implemented it. The tests have been taken care of in the previous incarnation of this PR (against the master branch), the feature seems to be working fine and it's designed in the most non-invasive way I could think of to minimize the chance of screwing other things up.

However, there wasn't a proper discussion of the implementation neither before nor after the PR, so I have no idea if it's good or bad or anything in between. There was this comment, but I'm not sure what LDM is, whether it covered the details of this PR and what to do next. So yes, some clarity and/or feedback would be nice =)

@gafter
Copy link
Member

gafter commented Jan 8, 2016

"LDM" is the (members of the) language design meeting.

We just looked at the proposal. It appears to be a breaking language change (i.e. will change the behavior of existing code), and there doesn't appear to be any nice way to fix that.

Here is an example of how the break arises. Given the two methods

void M(object) {}
void M<T, U>(T t) where T : IEnumerable<U> {}

and existing invocation

M("foo");

the previous version of the language (without this change) would call the former method. With the new rules in place, it would call the latter method.

Our customers tar and feather us when we take breaking changes, so this, unfortunately, might kill the idea of this feature.

Having said that, don't lose all hope. We (the LDM) are sad that we cannot take any breaking changes, even one such as this that seems like an improvement. We're wondering if it will be possible for us to make breaking changes if we also ship a tool that updates your code so that it has the same meaning as before. Having a mechanism for such a tool to exist would have made it easier for us to do many things in the past, and would have enabled us to avoid language-design contortions, for example due to new keywords (such as var, nameof and async) being identifiers in the previous version.

/cc @MadsTorgersen

@amcasey
Copy link
Member

amcasey commented Jan 8, 2016

The pull request that should fix Future is #7853.

@HellBrick
Copy link
Author

Ah, now I see it's way trickier than I expected =) Thanks for the explanation.

I perfectly understand how the backwards compatibility can sometimes be a burden, so I do hope that some story for mitigating breaking changes will be worked out eventually. Is there a public issue that tracks this idea? It would be a very interesting thing to follow.

@gafter gafter added 4 - In Review A fix for the issue is submitted for review. Area-Language Design labels Jan 9, 2016
@gafter gafter added this to the 2.0 milestone Jan 9, 2016
@gafter gafter added the Blocked label Jan 9, 2016
@BreyerW
Copy link

BreyerW commented Jan 9, 2016

As for breaking change - i wonder couldn't it be reduced by adding small constraint like that you have to say that you want to infer generic version. I think better to show example:

Instead of calling M("foo"); you have to write smth like M<,>("foo");. Just that you have to add <,> to tell compiler that you want to infer version with two generics. Or if we want to be more generic then <> or <var> would be enough to tell that we want best matched generic version with any number of generics

In this case calling M("foo"); still mean calling void M(object) {}

The problem is - will this constraint kill/greatly reduce usefulness of feature or not?

EDIT: Deleted line about standard resolution overload because i reminded that in c# situation with overloaded generic methods with same number of generics is unlikely (due to fact that generic constraint arent part of method signature)

@SergeyZhurikhin
Copy link

@gafter I accept, the call M("foo") as M<string, char>("foo").

@davkean
Copy link
Member

davkean commented Jan 13, 2016

(Closing/opening to sync update the merge branch to pick up the build break we just fixed)

@davkean
Copy link
Member

davkean commented Jan 13, 2016

@dotnet-bot retest this please

@gafter gafter modified the milestones: 2.0 (RC), 2.0 (Preview) Mar 7, 2016
@gafter
Copy link
Member

gafter commented Apr 28, 2016

There is no way for us to accept this change until we have a way of handling breaking changes, which will not be soon. So I'm going to close this PR. However, we are leaving it around for reference in case we can take it in the future.

@HellBrick Thanks for your contribution!

@Ultrahead
Copy link

@gafter: maybe M("Foo"); should throw a compiler error that could be solved with something like:

M<!>("Foo"); // calls the non-generic operation

M<>("Foo"); // call the generic operation

@YairHalberstadt
Copy link
Contributor

@Ultrahead
That would also be a breaking change.

@bahusoid
Copy link

bahusoid commented Sep 13, 2019

Is this issue forgotten? Why it's not considered for .NET Core 3.0?

I don't quite understand the argument about breaking changes. Can't it instead of silent behavior change throw some compilation "ambiguous extension method" exception. So end user when target framework is updated have to explicitly call proper generic/non-generic method and instead of foo.Method() have to call MyNonGenericExtensionsClass.Method(foo) or MyGenericExtensionClass.Method<Foo>(foo) for rare ambiguous cases. That's IMHO really little price for such a great improvement in framework.

@gafter
Copy link
Member

gafter commented Sep 16, 2019

@bahusoid This PR is a breaking change to the language, and has therefore been rejected by the LDM. If you have a different proposal we would consider it, but no alternate specification has been offered for us to consider. A specification that makes these cases ambiguous, as you suggest, would be a plausible approach if it doesn't make overload resolution much more complex.

In any case, language changes for .NET Core 3.0 were completed several months ago. It is too late to add to C# 8.0.

@symbiogenesis
Copy link

This would be a nice addition to C# 12 or 13

@phoyd
Copy link

phoyd commented Jun 28, 2024

Couldn't this simply be solved by specifying that U should be inferred from the where clause if not given explicitly? For example using a reserved word:

void M(object) {}
void M<T, U>(T t) where T : IEnumerable<U> where U : implicit {} // U is inferred from where clause
// alternatively: 
void M<T, implicit U>(T t) where T : IEnumerable<U> // U is inferred from where clause

This wouldn't break anything, would it? Just enable new functionality on new code.

@thomhurst
Copy link

Couldn't this simply be solved by specifying that U should be inferred from the where clause if not given explicitly? For example using a reserved word:

void M(object) {}
void M<T, U>(T t) where T : IEnumerable<U> where U : implicit {} // U is inferred from where clause
// alternatively: 
void M<T, implicit U>(T t) where T : IEnumerable<U> // U is inferred from where clause

This wouldn't break anything, would it? Just enable new functionality on new code.

I like this. By utilising that where clause you're opting into the behaviour.

@symbiogenesis
Copy link

Like the opt-in syntax as a compatibility solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
4 - In Review A fix for the issue is submitted for review. Area-Compilers Area-Language Design Blocked cla-already-signed Community The pull request was submitted by a contributor who is not a Microsoft employee.
Projects
None yet
Development

Successfully merging this pull request may close these issues.