[VOTE] First-class callable syntax

  115273
July 2, 2021 10:50 nikita.ppv@gmail.com (Nikita Popov)
Hi internals,

As the partial function application RFC has not been accepted, I have
opened voting on https://wiki.php.net/rfc/first_class_callable_syntax. The
vote closes on 2021-07-16.

This RFC uses a syntax that is forward-compatible with partial function
application. Should it not be accepted, I'll explore alternative syntax
possibilities.

Regards,
Nikita
  115276
July 2, 2021 16:31 ralph@ralphschindler.com (Ralph Schindler)
> This RFC uses a syntax that is forward-compatible with partial function > application. Should it not be accepted, I'll explore alternative syntax > possibilities.
Given the choice of syntax in this proposal (which I do like)... Is the following (potentially future) use case precluded by the syntax (whether or not you like it immediately ;) )? Basically be able to: 1. be able to refer to an instance method statically (this is currently an error) 2. allow late binding of $this/context/closure scope, either via bindTo() or via Reflection setClosureThis() (does not exist) class MyController { public function myAction($name) { return new Response("Hello, $name"); } } // Closure with no $this (to be filled in later) $fn = MyController->myAction(...); $controller = new MyController; $response = ($fn->bindTo($controller))($params); -ralph
  115279
July 2, 2021 22:21 internals@lists.php.net ("Levi Morrison via internals")
On Fri, Jul 2, 2021 at 10:32 AM Ralph Schindler
<ralph@ralphschindler.com> wrote:
> > > > This RFC uses a syntax that is forward-compatible with partial function > > application. Should it not be accepted, I'll explore alternative syntax > > possibilities. > > Given the choice of syntax in this proposal (which I do like)... Is the > following (potentially future) use case precluded by the syntax (whether > or not you like it immediately ;) )? > > Basically be able to: > > 1. be able to refer to an instance method statically (this is currently > an error) > > 2. allow late binding of $this/context/closure scope, either via > bindTo() or via Reflection setClosureThis() (does not exist) > > > > class MyController { > public function myAction($name) { > return new Response("Hello, $name"); > } > } > > // Closure with no $this (to be filled in later) > $fn = MyController->myAction(...); > > > $controller = new MyController; > $response = ($fn->bindTo($controller))($params);
It would theoretically be: $fn = MyController::myAction(...); It currently errors:
> Fatal error: Uncaught Error: Non-static method MyController::myAction() cannot be called statically
I would be okay with allowing this, as long as it's bound before it's called.
  115281
July 3, 2021 13:51 ralph@ralphschindler.com (Ralph Schindler)
> It would theoretically be: > > $fn = MyController::myAction(...); > > It currently errors: > >> Fatal error: Uncaught Error: Non-static method MyController::myAction() cannot be called statically > > I would be okay with allowing this, as long as it's bound before it's called.
One other question regarding the current patch. I have not seen this aspect discussed yet. From the list in the first proposal section (https://wiki.php.net/rfc/first_class_callable_syntax#proposal), I am focusing this question on the few that are by name (strings).. so: strlen(...); Foo::method(...); $classStr::$methodStr(...); self::{$complex . $expression}(...); 'strlen'(...); [Foo::class, 'method'](...); Do these have to scan the target function and its signature in order to wrap them? In the cases of classes above, this will trigger the autoloader. I wonder if it could create a by-name reference that resolves the signature at just in time / at call time in these situations? In context, suppose I am suggesting that something like: $routes = [ '/my-action' => MyController::myAction(...), '/other-action => [OtherController::class, 'otherAction'](...), ... ]; would not invoke the autoloader until one of them may be actually used/called. This would be similar in philosophy as to how Foo::class creates a fully qualified string without confirming a class exists at that name when the scanner and executor sees ::class. -ralph
  115284
July 3, 2021 15:07 nikita.ppv@gmail.com (Nikita Popov)
On Sat, Jul 3, 2021 at 3:51 PM Ralph Schindler <ralph@ralphschindler.com>
wrote:

> > > It would theoretically be: > > > > $fn = MyController::myAction(...); > > > > It currently errors: > > > >> Fatal error: Uncaught Error: Non-static method MyController::myAction() > cannot be called statically > > > > I would be okay with allowing this, as long as it's bound before it's > called. > > > One other question regarding the current patch. I have not seen this > aspect discussed yet. > > From the list in the first proposal section > (https://wiki.php.net/rfc/first_class_callable_syntax#proposal), I am > focusing this question on the few that are by name (strings).. so: > > strlen(...); > Foo::method(...); > $classStr::$methodStr(...); > self::{$complex . $expression}(...); > 'strlen'(...); > [Foo::class, 'method'](...); > > Do these have to scan the target function and its signature in order to > wrap them? In the cases of classes above, this will trigger the > autoloader. I wonder if it could create a by-name reference that > resolves the signature at just in time / at call time in these situations? > > In context, suppose I am suggesting that something like: > > $routes = [ > '/my-action' => MyController::myAction(...), > '/other-action => [OtherController::class, 'otherAction'](...), > ... > ]; > > would not invoke the autoloader until one of them may be actually > used/called. > > This would be similar in philosophy as to how Foo::class creates a fully > qualified string without confirming a class exists at that name when the > scanner and executor sees ::class. >
No, something like this will definitely not be supported as part of this syntax. It is fundamentally at odds with this proposal, which requires that callability is validated at point of creation, rather than at point of call (both of these may differ). Regards, Nikita
  115355
July 7, 2021 20:40 ralph@ralphschindler.com (Ralph Schindler)
> > It would theoretically be: > > $fn = MyController::myAction(...); > > It currently errors: > >> Fatal error: Uncaught Error: Non-static method MyController::myAction() cannot be called statically > > I would be okay with allowing this, as long as it's bound before it's called.
Could we do this for 8.1? Does something of this scope need an RFC? I started working up a patch over the weekend, it is 1/2 done (only 7 failing tests). I would really like to see this behavior come in with the (...) syntax, as I see it as pretty valuable. -ralph
  115356
July 7, 2021 21:03 nikita.ppv@gmail.com (Nikita Popov)
On Wed, Jul 7, 2021 at 10:41 PM Ralph Schindler <ralph@ralphschindler.com>
wrote:

> > > > > > It would theoretically be: > > > > $fn = MyController::myAction(...); > > > > It currently errors: > > > >> Fatal error: Uncaught Error: Non-static method MyController::myAction() > cannot be called statically > > > > I would be okay with allowing this, as long as it's bound before it's > called. > > Could we do this for 8.1? Does something of this scope need an RFC? > > I started working up a patch over the weekend, it is 1/2 done (only 7 > failing tests). > > I would really like to see this behavior come in with the (...) syntax, > as I see it as pretty valuable. >
I'm rather strongly opposed to this. MyController::myAction(...) is not callable, ergo trying to create a callable from it will fail. As I said before, performing the callability check at time of construction is a core part of this proposal, that I am not willing to compromise on. Regards, Nikita
  115357
July 7, 2021 21:06 internals@lists.php.net ("Levi Morrison via internals")
On Wed, Jul 7, 2021 at 2:40 PM Ralph Schindler <ralph@ralphschindler.com> wrote:
> > > > > > > It would theoretically be: > > > > $fn = MyController::myAction(...); > > > > It currently errors: > > > >> Fatal error: Uncaught Error: Non-static method MyController::myAction() cannot be called statically > > > > I would be okay with allowing this, as long as it's bound before it's called. > > Could we do this for 8.1? Does something of this scope need an RFC? > > I started working up a patch over the weekend, it is 1/2 done (only 7 > failing tests). > > I would really like to see this behavior come in with the (...) syntax, > as I see it as pretty valuable. > > -ralph
I think this requires an RFC. The behavior of `MyController::myAction(...)` should align with `Closure::fromCallable('MyController::myAction')`, and the latter is forbidden today. We should use an RFC to support the behavior in both places. We shouldn't try to sneak it in, especially as Nikita is opposed.
  115302
July 5, 2021 14:14 pollita@php.net (Sara Golemon)
On Fri, Jul 2, 2021 at 5:51 AM Nikita Popov ppv@gmail.com> wrote:

> As the partial function application RFC has not been accepted, I have > opened voting on https://wiki.php.net/rfc/first_class_callable_syntax. The > vote closes on 2021-07-16. > > This RFC uses a syntax that is forward-compatible with partial function > application. Should it not be accepted, I'll explore alternative syntax > possibilities. > > Was replying to a comment on reddit which made me look closer at your
implementation. It looks like this does NOT allow for use of function-like language constructs. e.g. $e = echo(...); $p = print(...); $r = require(...); // etc... 1/ Is this intentional? 2/ Is this actually a good thing? I'm actually of a mind that it's a good thing, as surprising ways to reach eval() are foot-guns waiting to happen, but wanted to get confirmation. -Sara
  115303
July 5, 2021 14:35 nikita.ppv@gmail.com (Nikita Popov)
On Mon, Jul 5, 2021 at 4:14 PM Sara Golemon <pollita@php.net> wrote:

> On Fri, Jul 2, 2021 at 5:51 AM Nikita Popov ppv@gmail.com> wrote: > >> As the partial function application RFC has not been accepted, I have >> opened voting on https://wiki.php.net/rfc/first_class_callable_syntax. >> The >> vote closes on 2021-07-16. >> >> This RFC uses a syntax that is forward-compatible with partial function >> application. Should it not be accepted, I'll explore alternative syntax >> possibilities. >> >> > Was replying to a comment on reddit which made me look closer at your > implementation. It looks like this does NOT allow for use of function-like > language constructs. > e.g. $e = echo(...); $p = print(...); $r = require(...); // etc... > > 1/ Is this intentional? >
Yes.
> 2/ Is this actually a good thing? >
Yes. echo is not a function, so you can't acquire a callable to it. The actual echo syntax is echo "Foo". PHP allows you to write echo("Foo") in the same way it allows you to write echo((((("Foo"))))). Don't do it :) Regards, Nikita
  115304
July 5, 2021 15:13 rowan.collins@gmail.com (Rowan Tommins)
On 05/07/2021 15:35, Nikita Popov wrote:
> The actual echo syntax is echo "Foo". PHP allows you to write echo("Foo") > in the same way it allows you to write echo((((("Foo"))))). Don't do it :)
Indeed. Note that this is valid: echo "hello", " ", "world"; But this is not: echo("hello", " ", "world"); I have in the past had a bug make it into production because the parentheses around a call to "include" were mis-placed because of this incorrect belief that it is "function-like": if ( include('some_file.php') && bar() ) Is equivalent to this (will always run bar(), then attempt to include either (string)true or (string)false): if ( include ('some_file.php' && bar()) ) When what was intended was this: if ( (include 'some_file.php') && bar() ) Regards, -- Rowan Tommins [IMSoP]
  115433
July 16, 2021 09:57 nikita.ppv@gmail.com (Nikita Popov)
On Fri, Jul 2, 2021 at 12:50 PM Nikita Popov ppv@gmail.com> wrote:

> Hi internals, > > As the partial function application RFC has not been accepted, I have > opened voting on https://wiki.php.net/rfc/first_class_callable_syntax. > The vote closes on 2021-07-16. >
The RFC has been accepted unanimously, with 44 votes in favor. Regards, Nikita