76 lines
4 KiB
Markdown
76 lines
4 KiB
Markdown
# 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.
|
|
|