[PHP-DEV] RFC: Support Closures in constant expressions

Hi

Am 2024-11-04 13:33, schrieb Rob Landers:

What would you expect the semantics of that script to be?

Isn't this semantically equivalent to:

Perhaps? That's the question I am asking. Given that this is not a valid PHP program as of now, it would as least be debatable how this is supposed to work. It gets funnier once you want to support variables in the general case (which I consider a necessary companion to support variable capturing):

     class Foo {
         public function __construct(&$out) {
             $out = 'out';
         }
     }

     function foo(
         string $bar,
         string $baz = new Foo($bar),
     ) {
         var_dump($bar, $baz);
     }

To be clear: We do not intend to solve this question with this RFC. These examples are just intended to highlight the (semantic) complexities of supporting variables within const-expr.

Best regards
Tim Düsterhus

On Mon, Nov 4, 2024, at 6:06 AM, Tim Düsterhus wrote:

Hi

Am 2024-10-31 07:16, schrieb Larry Garfield:

Hm. It would never occur to me to use a function for a non-class
constant in the first place, so I don't know. :slight_smile: Frankly I can't
recall the last time I used a non-class constant period. :slight_smile:

That said, PHP consts are already a bit squishy, and auto-capture is
always by value. That wouldn't give us a loophole?

Here's another brainteaser:

     function foo(
         string $bar,
         Closure $baz = static fn () => $bar,
     ) {
         var_dump($baz());
     }

     foo('captured');

What would you expect the semantics of that script to be?

My expectation is that it's confusing, and therefore we should simply disallow it. Which roughly translates to detecting if a closure tries to use an unbound variable statically. Which is probably more difficult than I make it sound. :slight_smile:

If it cannot reasonably be done now, but is possible, that should be
listed in future scope with roughly what it would take for a follow-up
to do. (And then we can argue if it should just be done now, with more
information as to how much work that is.)

I have added an entry to the “Future Scope” section. See also my reply
to Alex.

Best regards
Tim Düsterhus

Thanks. Can you include what the implementation challenges are for the future-scope items, to explain why they're being punted to later rather than addressed now? (As there seems clear interest in having all of them.)

--Larry Garfield

On Monday, 4 November 2024 at 20:32, Larry Garfield <larry@garfieldtech.com> wrote:

On Mon, Nov 4, 2024, at 6:06 AM, Tim Düsterhus wrote:
>
> Here's another brainteaser:
>
> function foo(
> string $bar,
> Closure $baz = static fn () => $bar,
> ) {
> var_dump($baz());
> }
>
> foo('captured');
>
> What would you expect the semantics of that script to be?

My expectation is that it's confusing, and therefore we should simply disallow it. Which roughly translates to detecting if a closure tries to use an unbound variable statically. Which is probably more difficult than I make it sound. :slight_smile:

Which is why short closures are being disallowed.
The value of it *is* limited, and the semantics of it are confusing.
I am struggling to see why this is such a big deal.
You know very well that PHP has tricky semantics that make figuring out "is this case ok but not this" near impossible.
Moreover, having short closure work in *some* but not *all* cases is also just hella confusing, banning it for all is clear and easy to reason about.

> > If it cannot reasonably be done now, but is possible, that should be
> > listed in future scope with roughly what it would take for a follow-up
> > to do. (And then we can argue if it should just be done now, with more
> > information as to how much work that is.)
>
> I have added an entry to the “Future Scope” section. See also my reply
> to Alex.

Thanks. Can you include what the implementation challenges are for the future-scope items, to explain why they're being punted to later rather than addressed now? (As there seems clear interest in having all of them.)

I have never seen a future scope section which describes these challenges, and this feels just giving more work to people for no benefit?
There has been clear intent on having Pattern matching and ADTs, yet they were split out because they are different scopes.
It is *fine* to have smaller incremental RFCs that build on top of each other without needing to provide justification as to why it was punted to later.
Arguably, the onus should be on people wanting it to be in the RFC to do the research and provide some implementation idea to have them be one RFC for this discussion to be at all reasonable.

Best regards,

Gina P. Banyard

On Mon, Nov 4, 2024, at 6:57 PM, Gina P. Banyard wrote:

On Monday, 4 November 2024 at 20:32, Larry Garfield
<larry@garfieldtech.com> wrote:

On Mon, Nov 4, 2024, at 6:06 AM, Tim Düsterhus wrote:
>
> Here's another brainteaser:
>
> function foo(
> string $bar,
> Closure $baz = static fn () => $bar,
> ) {
> var_dump($baz());
> }
>
> foo('captured');
>
> What would you expect the semantics of that script to be?

My expectation is that it's confusing, and therefore we should simply disallow it. Which roughly translates to detecting if a closure tries to use an unbound variable statically. Which is probably more difficult than I make it sound. :slight_smile:

Which is why short closures are being disallowed.
The value of it *is* limited, and the semantics of it are confusing.
I am struggling to see why this is such a big deal.

I suspect, since several people have asked about it, it's because most of the use cases for having a default Closure value are going to be short; the sort of short cases that short-closures and FCC are designed for. If it's long, then you're probably better off moving it to a private method and making a FCC reference to that private method the default value instead.

So if most of the use cases align with where one would use short-closures and FCC, not being able to is disappointing, even if the reasons for it are valid.

> > If it cannot reasonably be done now, but is possible, that should be
> > listed in future scope with roughly what it would take for a follow-up
> > to do. (And then we can argue if it should just be done now, with more
> > information as to how much work that is.)
>
> I have added an entry to the “Future Scope” section. See also my reply
> to Alex.

Thanks. Can you include what the implementation challenges are for the future-scope items, to explain why they're being punted to later rather than addressed now? (As there seems clear interest in having all of them.)

I have never seen a future scope section which describes these
challenges, and this feels just giving more work to people for no
benefit?

I'm not asking for a detailed description of the engine nuances of detecting unbound variables. Just something along the lines of "this would require detecting unbound variables, that's hard, so we'll save that for later." Which is a level of detail I frequently do provide in my RFC future-scopes.

--Larry Garfield

Hi

Am 2024-11-05 18:49, schrieb Larry Garfield:

My expectation is that it's confusing, and therefore we should simply disallow it. Which roughly translates to detecting if a closure tries to use an unbound variable statically. Which is probably more difficult than I make it sound. :slight_smile:

Which is why short closures are being disallowed.
The value of it *is* limited, and the semantics of it are confusing.
I am struggling to see why this is such a big deal.

I suspect, since several people have asked about it, it's because most of the use cases for having a default Closure value are going to be short; the sort of short cases that short-closures and FCC are designed for. If it's long, then you're probably better off moving it to a private method and making a FCC reference to that private method the default value instead.

Even though the initial example is one that uses a default parameter value, we see the primary use case in supporting Closures in attribute parameters. That’s why that’s the use case mentioned in the first paragraph and that’s why all the snippets in the “Use Cases” section showcase attributes.

I'm not asking for a detailed description of the engine nuances of detecting unbound variables. Just something along the lines of "this would require detecting unbound variables, that's hard, so we'll save that for later." Which is a level of detail I frequently do provide in my RFC future-scopes.

The extent of investigation I did regarding the implementation was verifying that “yes, it indeed does crash”, because you can’t build an implementation if you do not know what the expected behavior should be in the first place. It is not at all obvious how variable capturing (or rather: variables in general) should work from a semantic perspective, as you confirmed yourself (“it’s confusing, and therefore we should simply disallow it” [1]). We disagree with adding another special-case to PHP’s semantics by introducing a “non-capturing short closure” as a third type of Closure [2] and figuring out what the semantics of variables within const-expr should be is out of scope for this RFC.

In any case, the “Constraints” section already explains why those constraints exist.

Best regards
Tim Düsterhus

[1] My personal expectation for parameter default values would be that any previous parameter would be legal to access and capture.
[2] Or fourth, if you consider FCC to be a type of Closure.

Hi

Am 2024-10-29 16:11, schrieb Tim Düsterhus:

Volker and I would like to start discussion on our RFC to "Support Closures in constant expressions".

Please find the following resources for your reference:

- RFC: PHP: rfc:closures_in_const_expr
- Implementation: Allow defining Closures in const-expr by TimWolla · Pull Request #16458 · php/php-src · GitHub

The minimum 14 days of discussion will have passed in roughly 26 hours. Given that the discussion mostly revolved around the details of not supporting short closures and the RFC proposal has been unchanged except for some minor clarifications, we believe that everything important has been said and thus plan to open the vote some time on Wednesday, November 13.

Best regards
Tim Düsterhus