Add design notes

This commit is contained in:
Mads Torgersen 2018-05-23 09:37:18 -07:00
parent ba3a4cd71b
commit 2fa167f506

View file

@ -0,0 +1,76 @@
# C# Language Design Notes for May 21. 2018
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
# Target typed new
There's a PR for target typed `new` expressions
```c#
M(new (1));
```
We generally like it, and have been interested in a feature like this for a while, but we're a bit concerned about the ways in which use of it can be brittle. In the code above, adding another constructor to a type (which one of the overloads takes) can now cause an ambiguity, or cause a different overload to be picked.
It's roughly as "bad" as adding another implicit conversion.
We could consider restrictions. We could do it only in initializers, which is a common request. But that's still a harsh restriction. We would probably soon be back to considering the general version of the feature.
We could warn (or have an analyzer warn) if there is more than one possible target parameter type among overloads corresponding to a target-typed `new` argument.
We could also take a similar approach as we do to out vars. There we don't even take the parameter type into account for betterness purposes. We're ambiguous if two overloads differ only on the type of an out parameter, regardless of whether one is "better" than the other. If we do this similarly, then folks wouldn't easily get silent changes when overloads are added: instead they'd get ambiguity errors, and would be forced to put in a type name, or otherwise disambiguate.
## Conclusion
We would like to pursue this feature. Let's schedule a design meeting to make sure we have the design ironed out. We like the more restrictive approach to overload resolution, similar to out vars.
# Return/break/continue expressions
This is a convenience. However, it risks a syntactic conflict with other potential futures, especially "non-local returns" (allowing a lambda to return from its enclosing method) and "block expressions" (allowing statements inside expressions). While we can imagine syntaxes for those that do not conflict, we don't want to limit the design space for them at this point, at least not for a feature that is merely "nice to have".
Also, while we've been talking about this in analogy with throw expressions, that isn't quite right. `throw` is a dynamic effect, whereas `return`, `break` and `continue` are statically bound control transfers with a specific target.
## Conclusion
We're grateful for the pull request, but do not want to go forward with the feature at this point.
# Async streams
## Async foreach task consumption
Just like foreach it will look for an interface or a pattern. Should the pattern require `Task<T>`, or be pattern-based all the way down?
### Conclusion
Pattern all the way down. Same as with `await`.
## Async foreach extension methods
`foreach` doesn't allow extension methods for `GetEnumerator` etc. We would like to change that, but there are obscure potential breaking changes there. Should `foreach await` allow extension methods?
### Conclusion
Let's allow, and work to allow for synchronous `foreach` as well.
## Async iterator lambdas
We don't do iterator lambdas today, and it's low priority to add it, though it's not really harmful.
### Conclusion
We won't do it for async iterators either, until such time as we decide to do it for the synchronous ones.
## When is it an iterator?
In synchronous iterators, the `yield` keyword is what makes it an iterator. The same for async iterators: the combo of the `async` modifier and the presence of the `yield` keyword makes it an async iterator. Whether you actually await is at most the subject of a warning, just as withe other async methods, and has no other bearing.
## Pattern-based return type of async iterators?
Async methods no longer have to return `Task` and `Task<T>`, but can follow a pattern. We don't currently do a similar thing for iterators, but we should think about it, also for async iterators.
## foreach await over dynamic
Block it. For synchronous foreach we resort to the nongeneric `IEnumerable`, but there is no nongeneric `IAsyncEnumerable`, and there won't be.