On 6 March 2025 19:07:34 GMT, Larry Garfield <la...@garfieldtech.com> wrote:
>It is literally the same argument for "pass the DB connection into the 
>constructor, don't call a static method to get it" or "pass in the current 
>user object to the method, don't call a global function to get it."  These are 
>decades-old discussions with known solved problems, which all boil down to 
>"pass things explicitly."

I think the counterargument to this is that you wouldn't inject a service that 
implemented a while loop, or if statement. I'm not even sure what mocking a 
control flow primitive would mean.

Similarly, we don't pass around objects representing the "try context" so that 
we can call "throw"as a method on them. I'm not aware of anybody complaining 
that they can't mock the throw statement as a consequence, or wanting to work 
with multiple "try contexts" at once and choose which one to throw into.

A lexically scoped async{} statement feels like it could work similarly: the 
language primitive for "run this code in a new fiber" (and I think it should be 
a primitive, not a function or method) would look up the stack for an open 
async{} block, and that would be the "nursery" of the new fiber. [You may not 
like that name, but it's a lot less ambiguous than "context", which is being 
used for at least two different things in this discussion.]

Arguably this is even needed to be "correct by construction" - if the user can 
pass around nurseries, they can create a child fiber that outlives its parent, 
or extend the lifetime of one nursery by storing a reference to it in a fiber 
owned by a different nursery. If all they can do is spawn a fiber in the 
currently active nursery, the child's lifetime guaranteed to be no longer than 
its parent, and that lifetime is defined rigidly in the source code.

Rowan Tommins
[IMSoP]

Reply via email to