Re: PHP True Async RFC - Stage 2
| From: | Edmond Dantes | Date: | Wed, 19 Mar 2025 15:51:38 +0000 |
| Subject: | Re: PHP True Async RFC - Stage 2 | ||
| References: | 1 2 3 4 5 6 7 8 9 10 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-126846@lists.php.net to get a copy of this message | ||
>
> "Cheating" in the sense that you wrote out a "general syntax",
>
I got it.
> That's not the problem; the problem is that the following are all
equivalent expressions:
> (((foo())))
In principle, this is not a problem because the expression in parentheses
preceded by
spawn is unambiguously a call expression.
That is, from an analysis perspective, everything is fine here because the
expression spawn () is a syntax error.
On the other hand, the expression spawn (()) is also an error, but a
semantic one, because there is nothing to call.
The expression spawn ((), (), ()) is also an error because it attempts to
specify a list of parameters without a call expression.
It seems I didn't make any mistakes anywhere?
>
> But from a user's point of view, I hate rules like that which mean subtle
changes completely change the meaning of the code, in a very specific
context.
>
From the user's perspective, what is the difference between these two
expressions?
```php
spawn function(): string { ... };
spawn (function(): string { ... });
```
>
> All of these are examples of the keyword being followed by *a function
call*, not *any expression*. Which honestly I think is the right way to go.
>
I created these examples according to the syntax rules from the Bison file
for function_call.
Of course, I might have missed something, but overall, the list above
essentially represents what function_call should expand into.
If we define the rule spawn function_call, then spawn acts as a prefix
in this expression. However, everything that is already defined for
function_call in PHP must remain valid.
If we take the most complex part:
```bison
| callable_expr { $<num>$ = CG(zend_lineno); } argument_list {
$$ = zend_ast_create(ZEND_AST_CALL, $1, $3);
$$->lineno = $<num>2;
}
callable_expr:
callable_variable { $$ = $1; }
| '(' expr ')' { $$ = $2; }
| dereferenceable_scalar { $$ = $1; }
| new_dereferenceable { $$ = $1; }
;
```
we can see that the expression (((some()))) corresponds to the second line,
and arguments must follow.
However, in this case, arguments become *mandatory*. This means that after
(((some()))), there must also be ().
And another good thing about this syntax: a coroutine is an execution
context, and a function call is a transition to a block of code.
This means that the expression spawn function_call can be understood
literally as:
* Create a new execution context
* Execute function_call
The syntax for function_call is already defined in PHP, and nothing
changes in it.
The second form of the spawn expression is:
```php
spawn inline_function
```
where inline_function is a definition from bison:
```bison
inline_function:
function returns_ref backup_doc_comment '(' parameter_list ')' lexical_vars
return_type
...;
```
Another advantage of this solution is that if function_call constructs
change in the future, we won’t need to modify the definitions for spawn.
>
> If it's going to be a special case for an "inline coroutine", just use a
keyword other than "function", so it doesn't look like an expression when
it's not, like "spawn block { ... }"; or no keyword at all, just "spawn {
... }"
>
Well, yes, but here we again face the issue with returnType syntax, which
ends up hanging in the air...