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]