csharplang/meetings/2017/LDM-2017-08-30.md
2017-10-05 20:15:39 -07:00

3.5 KiB

C# Language Design Notes for Aug 30, 2017

Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!

IAsyncDisposable

IAsyncDisposable is sort of required, due to the way we translate foreach. Also it's useful in a lot of situations.

Don't take a CancellationToken, since when you cancel an operation you don't want to also cancel the clean-up.

Contract should be similar to Dispose, where you can call multiple times (sequentially) without problem.

If an enumerator (for instance) implements both sync and async, the client is only required to call one of them. The expansion of foreach will prefer sync or async naturally through the expansion.

Does the CT given to an enumerator apply during disposal? That would need to be up to the implementation. Standard guidance would be not to cancel any of what the disposal does. Advanced implementors might use the fact that the enumerator was canceled to skip some work.

Alternative IAsyncEnumerable pattern

There's an alternative that does "explicit chunking", by asynchronously yielding synchronous enumerables, to be consumed in a nested loop.

Tests show that this is a lot more efficient than the simple design.

We are cautiously leaning in this direction. As long as we can work out iterators, which are not fully explored.

ConfigureAwait

Can use extension methods on IAE

foreach

Proposal to support foreach over enumera_tors_, not just enunera_bles_.

Should enumerators be first class currency? There's a path we can take where everything is still enumerables, and you can get enumerables from other enumerables representing e.g. something with a CT, a certain starting point, etc.

Downsides: If you're just foreaching over an enumerator, is it your responsibility to dispose it? Also, should LINQ then be implemented over those as well?

Let's not open that Pandora's box for now. Let's stick with enumerable.

Consider a ToEnumerable on enumerators.

Syntax options

Need to think about it in connection with using also.

We'll stick with foreach await ( ... ) for now.

pattern-based

Similar to today. We get that one layer of optimization. There's more wrapping here (MoveNextAsync, WithCancellation), so we quickly still end up with interface dispatch.

iterators

Same as current iterators, except with an async keyword, and IAsyncEnumerable/tor return type. Needs to feel exactly like putting iterators and async methods together.

Could consider custom builders for this. Skip for now.

If we do the fancier version of IAE we need to find out how to compile. Need to spec the contract completely and then follow that. (Probably some sequences of calls would be unspecified).

Cancellation

There are two competing models around this.

  1. Take CT into GetEnumerator, and find a way to syntactically expose it in an iterator body
  2. Don't pass CT's into the enumerator at all; pass them to iterator methods

We actually want to start out with 2, and see if that gets us into trouble

LINQ

There are 200 overloads on enumerable, and most would need to be duplicated on IAE. Then, mixing sync and async would add another axis of this.

Ix has an implementation of these already, and they would adjust to what we decide.

If you have that, query expressions would work to some degree, when the bodies are sync.

However, we don't currently allow await in query clauses, because the lambdas we target don't have the async modifier.

Fine to not do it now. At some point we would want to deeply investigate how to get those awaits in.