>  One key question, if we disallow explicitly creating Fibers inside an
async block,
> can a Fiber be created outside of it and not block async, or would that
also be excluded?  Viz, this is illegal:
>

Creating a `Fiber` outside of an asynchronous block is allowed; this
ensures backward compatibility.
According to the logic integrity rule, an asynchronous block cannot be
created inside a Fiber. This is a correct statement.

However, if the asynchronous block blocks execution, then it does not
matter whether a Fiber was created or not, because it will not be possible
to switch it in any way.
So, the answer to your question is: yes, such code is legal, but the Fiber
will not be usable for switching.

In other words, Fiber and an asynchronous block are mutually exclusive.
Only one of them can be used at a time: either Fiber + Revolt or an
asynchronous block.

Of course, this is not an elegant solution, as it adds one more rule to the
language, making it more complex. However, from a legacy perspective, it
seems like a minimal scar.

(to All: Please leave your opinion if you are reading this )

>  // This return statement blocks until foo() and bar() complete.

Yes, *that's correct*. That's exactly what I mean.

Of course, under the hood, return will execute immediately if the coroutine
is not waiting for anything. However, the Scheduler will store its result
and pause it until the child coroutines finish their work.

In essence, this follows the parent-child coroutine pattern, where they are
always linked. The downside is that it requires more code inside the
implementation, and some people might accuse us of a paternalistic
approach. :)

>
> should consider if async {} makes sense to have its own catch and finally
blocks built in.)
>
We can use the approach from the RFC to catch exceptions from child
coroutines: explicit waiting, which creates a handover point for exceptions.

Alternatively, a separate handler like Context::catch() could be
introduced, which can be defined at the beginning of the coroutine.

Or both approaches could be supported. There's definitely something to
think about here.

>
>  That is still dependency injection, because ThingRunner is still taking
all of its dependencies via the constructor.  And being readonly, it's
still immutable-friendly.
>

Yeah, so basically, you're creating the service again and again for each
coroutine if the coroutine needs to use it. This is a good solution in the
context of multitasking, but it loses in terms of performance and memory,
as well as complexity and code size, because it requires more factory
classes.

The main advantage of *LongRunning* is initializing once and using it
multiple times. On the other hand, this approach explicitly manages memory,
ensuring that all objects are created within the coroutine's context rather
than in the global context.

Ah, now I see how much you dislike global state! :)

However, in a scenario where a web server handles many similar requests,
"global state" might not necessarily win in terms of speed but rather due
to the simplicity of implementation and the overall maintenance cost of the
code. (I know that in programming, there is an entire camp of immutability
advocates who preach that their approach is the key remedy for errors.)

I would support both paradigms, especially since it doesn’t cost much.

A coroutine will own its internal context anyway, and this context will be
carried along with it, even across threads. How to use this context is up
to the programmer to decide.  But at the same time, I will try to make the
pattern you described fit seamlessly into this logic.

Ed.

Reply via email to