If I say it's bright, you call it dark.
If I choose the east, you push for the south.
You’re not seeking a path, just a fight...
Debating with you? Not worth the time!

Em ter., 18 de mar. de 2025 às 03:00, Larry Garfield <la...@garfieldtech.com>
escreveu:

> On Sun, Mar 16, 2025, at 4:24 AM, Edmond Dantes wrote:
> > Good day, everyone. I hope you're doing well.
> >
> > https://wiki.php.net/rfc/true_async
> >
> > Here is a new version of the RFC dedicated to asynchrony.
> >
> > Key differences from the previous version:
> >
> > * The RFC is not based on Fiber; it introduces a separate class
> > representation for the asynchronous context.
>
> I'm unclear here.  It doesn't expose Fibers at all, or it's not even
> touching the C code for fibers internally?  Like, would this render the
> existing Fiber code entirely vestigial, not just its API?
>
> > * All low-level elements, including the Scheduler and Reactor, have
> > been removed from the RFC.
> > * The RFC does not include Future, Channel, or any other primitives,
> > except those directly related to the implementation of structured
> > concurrency.
> >
> > The new RFC proposes more significant changes than the previous one;
> > however, all of them are feasible for implementation.
> >
> > I have also added PHP code examples to illustrate how it could look
> > within the API of this RFC.
> >
> > I would like to make a few comments right away. In the end, the Kotlin
> > model lost, and the RFC includes an analysis of why this happened. The
> > model that won is based on the Actor approach, although, in reality,
> > there are no Actors, nor is there an assumption of implementing
> > encapsulated processes.
> >
> > On an emotional level, the chosen model prevailed because it forces
> > developers to constantly think about how long coroutines will run and
> > what they should be synchronized with. This somewhat reminded me of
> > Rust’s approach to lifetime management.
>
> Considering that lifetime management is one of the hardest things in Rust
> to learn, that's not a ringing endorsement.
>
> > Another advantage I liked is that there is no need for complex syntax
> > like in Kotlin, nor do we have to create separate entities like
> > Supervisors and so on. Everything is achieved through a simple API that
> > is quite intuitive.
>
> I'll be honest... intuitive is not the term I'd use.  In fact, I didn't
> make it all the way through the RFC before I got extremely confused about
> how it all worked.
>
> First off, it desperately needs an "executive summary" section up at the
> top.  There's a *lot* going on, and having a big-picture overview would
> help a ton.  (For examples, see property hooks[1] and pattern
> matching[2].)
>
> Second, please include realistic examples.  Nearly all of the examples are
> contrived, which doesn't help me see how I would actually use async
> routines or what the common patterns would be, and I therefore cannot
> evaluate how well the proposal treats those common cases.  The first
> non-foobar example includes a comment "of course you should never do it
> like this", which makes the example rather useless.  And the second is
> built around a code model that I would never, ever accept into a code base,
> so it's again unhelpful.  Most of the RFC also uses examples that... have
> no return values.  So from reading the first half of it, I honestly
> couldn't tell you how return values work, or if they're wrapped in a Future
> or something.
>
> Third, regarding syntax, I largely agree with Tim that keywords are better
> than functions.  This is very low-level functionality, so we can and should
> build dedicated syntax to make it as robust and self-evident (and IDE
> friendly) as possible.
>
> That said, even allowing for the async or await or spawn keywords, I got
> super confused when the Scope object was introduced.  So would the
> functions/keywords be shortcuts for some of the common functionality of a
> Scope object?  If not, what's the actual difference?  I got lost at that
> point.
>
> The first few sections of the RFC seem to read as "this RFC doesn't
> actually work at all, until some future RFC handles this other part."
> Which... no, that's not how this works. :-)
>
> As someone that has not built an async framework before (which is 99.9% of
> PHP developers, including those on this list), I do not see the point of
> half the functionality here.  Especially the BoundedScope.  I see no reason
> for it to be separate from just any other Scope.  What is the difference
> between scope and context?  I have no clue.
>
> My biggest issue, though, is that I honestly can't tell what the mental
> model is supposed to be.  The RFC goes into detail about three different
> async models.  Are those standard terms you're borrowing from elsewhere, or
> your own creation?  If the former, please include citations.  I cannot
> really tell which one the "playpen" model would fit into.  I... think
> bottom up, but I'm not sure.  Moreover, I then cannot tell which of those
> models is in use in the RFC.  There's a passing reference to it being
> bottom up, I think, but it certainly looks like the No Limit model.
> There's a section called structured concurrency, but what it describes
> doesn't look a thing like the playpen-definition of structured concurrency,
> which as noted is my preference.  It's not clear why the various positives
> and negatives are there; it's just presented as though self-evident.  Why
> does bottom up lead to high memory usage, for instance?  That's not clear
> to me.  So really... I have no idea how to think about any of it.
>
> Sorry, I'm just totally lost at this point.
>
> As an aside: I used "spawn" as a throw-away keyword to avoid using "await"
> in a previous example.  It's probably not the right word to use in most of
> these cases.
>
> I know some have expressed the sentiment that tightly structured
> concurrency is just us not trusting developers and babysitting them.  To
> which I say... YES!  The overwhelming majority of PHP developers have no
> experience writing async code.  Their odds of getting it wrong and doing
> something inadvertently stupid by accident through not understanding some
> nuance are high.  And I include myself in that.  MY chances of
> inadvertently doing something stupid by accident are high.  I *want* a
> design that doesn't let me shoot myself in the foot, or at least makes it
> difficult to do.  If that means I cannot do everything I want to... GOOD!
> Humans are not to be trusted with manually coordinating parallelism.  We're
> just not very good at it, as a species.
>
> Broadly speaking, I can think of three usage patterns for async in PHP
> (speaking, again, as someone who doesn't have a lot of async experience, so
> I may be missing some):
>
> 1. Fan-out.  This is the "fetch all these URLs at once" type use case,
> which in most cases could be wrapped up into a para_map() function.  (Which
> is exactly what Rust does.)
> 2. Request handlers, for persistent-process servers.  Would also apply for
> a queue worker.
> 3. Throw it over the wall.  This would be the logging example, or sending
> an email on some trigger, etc.  Importantly, these are cases where there is
> no result needed from the sub-routine.
>
> I feel like those three seem to capture most reasonable use cases, give or
> take some details.  (And, of course, many apps will include all three in
> various places.)  So any proposal should include copious examples of how
> those three cases would look, and why they're sufficiently ergonomic.
>
> A playpen model can handle both 1 and 2.  In fan out, you want the "Wait
> all" logic, but then you also need to think about a Future object or
> similar.  In a request handler, you're spawning an arbitrary number of
> coroutines that will terminate, and you probably don't care if they have a
> return value.
>
> It's the "throw over the wall" cases where a playpen takes more work.  As
> I showed previously, it can be done.  It just takes a bit more setup.  But
> if that is too much for folks, I offer a compromise position.  Again, just
> spitballing the syntax specifics:
>
> // Creates an async scope, in which you can create coroutines.
> async {
>
>   // Creates a new coroutine that MAY last beyond the scope of this block.
>   // However, it MUST be a void-return function, indicating that it's
> going to
>   // do work that is not relevant to the rest of this block.
>   spawn func_call(1, 2, 3);
>
>   // Creates a new coroutine that will block at the end of this async
> block.
>   // The return value is a future for whatever other_function() will
> return.
>   // $future may be used as though it were the type returned, but trying
>   // to read it will block until the function completes.  It may also have
> other
>   // methods on it, not sure.
>   $future = start other_function(4, 5, 6);
>
>   // Queues a coroutine to get called after all "start"ed coroutines have
> completed
>   // and this block is about to end.  Its return value is discarded.
> Perhaps it should be
>   // restricted to void-return, not sure.  In this case it doesn't hurt
> anything.
>   defer cleanup(7, 8, 9);
>
>   // Do nothing except allow other coroutines to switch in here if they
> want.
>   suspend;
>
>   // Enqueues this coroutine to run in 100 ms, or slightly thereafter
> whenever the scheduler gets to it.
>   timeout 100ms something(4, 5, 6);
>
> } // There is an implicit wait-all here for anything start-ed, but not for
> spawn-ed.
>
> I honestly cannot see a use case at this point for starting coroutines in
> arbitrary scopes.  Only "current scope" and "global scope, let it escape."
> That maps to "start" and "spawn" above.  If internally "spawn" gets
> translated to "start in the implicit async block that is the entire
> application", so that those coroutines will still block the whole script
> from terminating, that is not a detail most devs will care about.  (Which
> also means in the global async scope, spawn and start are basically
> synonymous.)
>
> I can see the need for cancellation, which means probably we do need a
> scope object to represent the current async block.  However, that's just a
> cancel() method, which would propagate to any child.  Scheduling it can be
> handled by the timeout command.  At this point, I do not see the use case
> for anything more advanced than the above (except for channels, which as I
> argued before could make spawn unnecessary).  There may be a good reason
> for it, but I don't know what it is and the RFC does not make a compelling
> argument for why anything more is needed.
>
> I could see an argument that async $scope { ... } lets you call all of the
> above keywords as methods on $scope, and the keywords are essentially a
> shorthand for "this method on the current scope".  But you could also pass
> the scope object around to places if you want to do dangerous things.  I'm
> not sure if I like that, honestly, but it seems like an option.
>
> Elsewhere in the thread, Tim noted that we should unify the function call
> vs closure question.  I used straight function calls above for simplicity,
> but standardizing on a closure also makes sense.  Related, I've been
> talking with Arnaud about trying to put Partial Function Application
> forward again[3], assuming pipes[4] pass.  If we follow the previous model,
> then it would implicitly provide a way to turn any function call into a
> delayed function call:
>
> function foo(int $a, int $b) { ... }
>
> foo(4, 5); // Calls foo() right now
> foo(4, 5, ...); // Creates a 0-argument closure that will call foo(4, 5)
> when invoked.
>
> Basically the latter is equivalent to:
> fn() => foo(4, 5);
>
> A 0-argument closure (because all arguments are already captured) goes by
> the delightful name "thunk" (as in the past tense of think, if you don't
> know English very well.)  That likely wouldn't be ideal, but it would make
> standardizing start/spawn on "thou shalt provide a closure" fairly
> straightforward, as any function could trivially be wrapped into one.
>
> That's not necessarily the best way, but I mention it to show that there
> are options available if we allow related features to support each other
> synergistically, which I always encourage.
>
> Like Tim, I applaud you're commitment to this topic and willingness to
> work with feedback.  But the RFC text is still a long way from a model that
> I can wrap my head around, much less support.
>
> [1] https://wiki.php.net/rfc/property-hooks
> [2] https://wiki.php.net/rfc/pattern-matching
> [3] https://wiki.php.net/rfc/partial_function_application
> [4] https://wiki.php.net/rfc/pipe-operator-v3
>
> --Larry Garfield
>

Reply via email to