First Draft: Coroutines

Sebastiaan Koppe mail at skoppe.eu
Tue Sep 24 10:47:28 UTC 2024


On Tuesday, 6 August 2024 at 02:20:52 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
> On 06/08/2024 3:50 AM, Sebastiaan Koppe wrote:
>> On Monday, 5 August 2024 at 01:36:31 UTC, Richard (Rikki) 
>> Andrew Cattermole wrote:
>>> Coroutines is a stack machine transformed representation for 
>>> a function. It enables storing the state of a function 
>>> externally to the stack to allow for high throughput event 
>>> handling.
>> 
>> Thank you for spearheading this. Having coroutines in the 
>> language would be a big step forward, and hopefully pave the 
>> way for more non-blocking programming in D.
>> 
>> There are a few points I would like to bring up early though.
>> 
>> - I see no mention of C++'s coroutines. I think it would be 
>> good to learn from their design and implementation.
>>
>> - As you might know I am a big proponent of C++'s 
>> Senders/Receivers (a.k.a. P2300). One awesome integration they 
>> have is that Senders can be awaited by coroutines, and 
>> coroutines can await Senders. This allows for users to pick 
>> their preference, e.g. use convenient coroutines but suffer 
>> some allocation costs, and use Senders for more performant 
>> sections if need be. See
>> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2300r7.html#design-awaitables-are-senders and https://ericniebler.com/2024/02/04/what-are-senders-good-for-anyway/
>
> My conclusion about P2300 is that it is all library code. This 
> DIP would allow you to implement it.

https://github.com/symmetryinvestments/concurrency already 
implements a fairly large chunk of it.

I suppose what you mean is that this DIP would allow me to 
integrate coroutines with it.

>> - I don't understand the choice for `@async`. Sometimes 
>> resembling the word "coroutine" would be better in my opinion.
>
> It was ``@co`` for a long time.
>
> I had a number of people request ``async`` instead.
>
>> - In `while(Future!string readLine = socket.readUntil("\n"))` 
>> I suspect it to do an explicit await. How does that work?
>
> When a coroutine is acquired, it'll yield automatically, it is 
> implicit.

What do you mean with 'acquired'?

>> - I find the use of `Future` a bit confusing. Futures 
>> generally carry too much synchronisation overhead with them 
>> and I doubt coroutines need a full Future implementation 
>> anyway. Perhaps call it a Task like they do in C++?
>
> This DIP does not propose library code. You are free to write 
> whatever library code you want and have it construct itself 
> from the language representation of a coroutine.

I fail to see what it then _does_ provide.

> This is a key design goal as there is a good chance we'll want 
> different solutions for different tasks.

It ultimately needs to integrate with the language coroutine 
support, so I don't understand how it can be completely separate. 
And if it can't, then the language needs to provide some 
low-level interface to it, which, granted, libraries can extend 
on.

>> - The use of `@isasync` is a bit too cute for me. It also 
>> hides the fact sometime is a coroutine.
>
> I want to be placing a lot more emphasis on UDA's like 
> ``@Route``, what we do right now with reflection is far too 
> costly in terms of how to register symbols.

I think it distracts from the proposal. Likely `@Route` will be 
implemented in a library anyway.

> But yes, I do want to hide that it is a coroutine. The average 
> person shouldn't have to care if its asynchronous of 
> synchronous, it does not enable them to archive business goals 
> faster.

I very much like the low-level/high-level proposed approach to 
Phobos3. I very much do not want the language to hide coroutine 
details, and instead want to be in full control, when and if a 
coroutine is called/yielded to.

>> - Instead of allowing a coroutine in a function that is not a 
>> coroutine itself - and injecting a blocking call - I would 
>> require explicit call to evaluate the coroutine instead. No 
>> magic.
>
> And that is library code ;)
>
> But yes, I did try to explain that you did not need language 
> integration for this. In fact the prime sieve example show 
> cases exactly what you suggest!

I do not understand what it does - and how! It is unclear to me 
how `&generate` is turned into a `InstantiableCoroutine!(int)`, 
how `makeInstance` works (or what it even does) and what 
`ch.block` does.

I can guess of course, but that explanation needs to be part of 
the DIP.



More information about the dip.development mailing list