On Fri, Mar 7, 2025, at 3:39 AM, Rowan Tommins [IMSoP] wrote: > 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]
Since I think better in code, if using try-catch as a model, that would lead to something like: function foo(int $x): int { // if foo() is called inside an async block, this is non-blocking. // if it's called outside an async block, it's blocking. syslog(__FUNCTION__); return 1; } function bar(int $x): int { return $x + 1; // Just a boring function like always. } function baz(int $x): int { // Because this is called here, baz() MUST only be called from // inside a nested async block. Doing otherwise cause a fatal at runtime. spawn foo($x); } async { // Starts a nursery $res1 = spawn foo(5); // Spawns new Fiber that runs foo(). $res2 = spawn bar(3); // A second fiber. $res3 = spawn baz(3); // A Third fiber. // merge results somehow return $combinedResult; } // We block here until everything spawned inside this async block finishes. spawn bar(3); // This is called outside of an async() block, so it just crashes the program (like an uncaught exception). Is that what you're suggesting? If so, I'd have to think it through a bit more to see what guarantees that does[n't] provide. It might work. (I deliberately used spawn instead of "await" to avoid the mental association with JS async/await.) My biggest issue is that this is starting to feel like colored functions, even if partially transparent. --- Another point worth mentioning: I get the impression that there are two very different mental models of when/why one would use async that are floating around in this thread, which lead to two different sets of conclusions. 1. Async in the small: Like the reporting example, "fan out" a set of tasks, and bring them back together quickly before continuing in an otherwise mostly sync PHP-FPM process. All the data is still part of one user request, so we still have "shared nothing." 2. Async in the large: A long running server like Node.js, ReactPHP, etc. Multiplexing several user requests into one OS process via async on the IO points. Basically the entire application has a giant async {} wrapped around it. Neither of these is a bad use case, and they're not mutually exclusive, but they do lead to different priorities. I freely admit my bias is towards Type 1, while it sounds like Edmond is coming from a Type 2 perspective. Not a criticism, just flagging it as something that we should be aware of. --Larry Garfield