Re: PHP True Async RFC - Stage 2
| From: | Rowan Tommins [IMSoP] | 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 internals+get-126814@lists.php.net to get a copy of this message | ||
On 17/03/2025 07:58, Edmond Dantes wrote:
However, I really liked theI 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$scope->spawn()construct in the example code, as it feels the most natural compared tospawn in. Moreover, thespawn inexpression is quite complex to implement, but I don't have enough experience to evaluate it properly.
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 theI 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();suspendkeyword. However, thedeferkeyword 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 consideredfinally, but it is already used in thetryblock.
The implementation also concerns me a bit. It seems that to fully implement theI'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?)deferblock, we would need something similar tofinally, or essentially makedefercreate an implicittry...finallyblock.
**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 formIs this just a description of your own comprehension, or based on some more general experience of something similar?spawn <callable>(<parameters>);is a shorthand forspawn use(<callable>, <parameters>) { return ... };The expression<callable>(<parameters>);is not executed directly at the point wherespawnis 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.
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]