Re: PHP True Async RFC - Stage 2

From: Date: Mon, 17 Mar 2025 23:08:34 +0000
Subject: Re: PHP True Async RFC - Stage 2
References: 1 2 3  Groups: php.internals 
Request: Send a blank email to [email protected] to get a copy of this message
On 17/03/2025 07:58, Edmond Dantes wrote:
However, I really liked the $scope->spawn() construct in the example code, as it feels the most natural compared to spawn in. Moreover, the spawn in expression is quite complex to implement, but I don't have enough experience to evaluate it properly.
I agree, it's a natural way of making the spawn happen "on" the scope; but it potentially makes spawning in a scope a "second-class citizen" if spawn foo($bar); has to be expanded out to as `$scope->spawn( fn() => foo($bar) );` just to add a scope. There are other ways it could be included, like with some extra punctuation: spawn($scope) foo($bar); spawn<$scope> foo($bar); spawn@$scope foo($bar);
## Defer I have nothing against the suspend keyword. However, the defer keyword raises some questions. "Defer" means to postpone something (to delay execution). But in this case, it’s not about "postponing" but rather "executing upon function block exit." I don't know why the creators of Go chose this word. I considered finally, but it is already used in the try block.
I did say the names were subject to bikeshedding; my main point was that this was one of the actions that should have a keyword. I mostly chose "defer" because it's what used in other languages, and "onexit" sounds like "run at end of program". That said, "defer" makes perfect sense to me: the action is not run immediately, it's delayed (deferred) until the end of the scope. do_this_first(); defer do_this_later(); do_this_second();
The implementation also concerns me a bit. It seems that to fully implement the defer block, we would need something similar to finally, or essentially make defer create an implicit try...finally block.
I'm confused - $scope->onExit() is already in the RFC, and I wasn't suggesting any change other than the syntax. (Although I'm not sure if it should defer to coroutine exit rather than scope exit by default?)
**General syntax:**
spawn [in <scope>] function [use(<parameters>)][: <returnType>] {
<codeBlock>
};
The "function" keyword just looks out of place here, because there's never a function the user can see. I also don't like that it's almost-but-not-quite a valid closure expression - the missing () looks like a typo. If the syntax is going to be that close, why not just allow an actual callable? That way, a user can write an anonymous function with all the features already supported - return types, captured variables (you've labelled them "parameters" here, but that's not what a "use" statement lists), etc. In an earlier draft of my e-mail, I was going to suggest a "spawn call" variant, where: spawn foo(42); // spawns a call to foo with argument 42 spawn call $callable; // spawns a call to whatever's in $callable, with no arguments spawn call function() use($foo, &$bar) { do_whatever($foo, $bar); }; // creates a Closure, and spawns a call to it spawn call bar(...); // mostly equivalent to "spawn bar();", but with some extra overhead spawn call create_me_a_lovely_function('some', 'args'); // calls the function directly, then asserts that the result is a callable, and spawns a call to that with no arguments Or maybe they're just two different keywords: async_run foo(42); async_call $callable; In general, I prefer code to be explicit and unambiguous at a glance, rather than concise but ambiguous unless you've memorised the grammar. So if there are two forms of "spawn", I'd prefer to spell them differently.
The form spawn <callable>(<parameters>); is a shorthand for spawn use(<callable>, <parameters>) { return ... }; The expression <callable>(<parameters>); is not executed directly at the point where spawn is used but in a different context. There is a slight logical ambiguity in this form, but it does not seem to cause any issues with comprehension.
Is this just a description of your own comprehension, or based on some more general experience of something similar?
As for the form: spawn (<expression>)(parameters) — I suggest not implementing it at all.
I'm not sure what you mean by <callable> above. Slightly expanding out the actual parser rules from php-src, a function_call can be: name argument_list class_name T_PAAMAYIM_NEKUDOTAYIM member_name argument_list variable_class_name T_PAAMAYIM_NEKUDOTAYIM member_name argument_list callable_variable argument_list dereferenceable_scalar argument_list new_dereferenceable argument_list '(' expr ')' argument_list Where callable_variable is a slightly misleading name, and includes expanding recursively to function_call, as in the add(1)(2) form beloved of Function Programmers Is there a reason to redefine all of this and make fresh decisions about what to allow? I would argue for "principle of least surprise": reuse or emulate as much of the existing grammar as possible, even if you personally would never use it. -- Rowan Tommins [IMSoP]

Thread (59 messages)

« previous php.internals (#126814) next »