Hi
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: https://wiki.php.net/rfc/closures_in_const_expr
 - Implementation: https://github.com/php/php-src/pull/16458
 
Best regards 
Tim Düsterhus
On Tuesday, 29 October 2024 at 18:14, Tim Düsterhus tim@bastelstu.be 
wrote:
Hi
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: https://wiki.php.net/rfc/closures_in_const_expr
 - Implementation: https://github.com/php/php-src/pull/16458
 Best regards
Tim Düsterhus
Hi, Tim!
Looking forward to this feature! Good luck with the RFC.
Will the first class callable syntax be supported?
#[Serialize\Custom(strtoupper(...))]
-- 
Valentin
Hi
Am 2024-10-29 21:12, schrieb Valentin Udaltsov:
Will the first class callable syntax be supported?
#[Serialize\Custom(strtoupper(...))]
It is currently listed in the “Future Scope” section. Mainly, because 
the implementation does not trivially extend to first-class callables. 
When you have a regular Closure, everything you need to know is right 
inside the file. For first-class callables the target function might 
reside in a different file and already needs to be available. For static 
methods, the autoloader might need to be called. So first-class 
callables behave similarly to new-expressions, whereas Closures behave 
more like string literals or number literals.
Best regards 
Tim Düsterhus
Hi
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: https://wiki.php.net/rfc/closures_in_const_expr
 - Implementation: https://github.com/php/php-src/pull/16458
 Best regards
Tim Düsterhus
This seems like a good idea to me. My only real question is why we need to forbid short-closures. I fully agree that capturing variables for such functions doesn't work. What I don't understand is why that precludes short-closures. Is it not possible to "just" say "there's nothing to even capture in this context, don't try"? (There may be technical reasons for that, but I do not know what they are and the RFC doesn't say.)
--Larry Garfield
Hi
Am 2024-10-30 05:25, schrieb Larry Garfield:
This seems like a good idea to me. My only real question is why we
need to forbid short-closures. I fully agree that capturing variables
for such functions doesn't work. What I don't understand is why that
precludes short-closures. Is it not possible to "just" say "there's
nothing to even capture in this context, don't try"? (There may be
technical reasons for that, but I do not know what they are and the RFC
doesn't say.)
It would indeed require some special handling to disable the 
auto-capturing in the code. This would be solvable of course, but 
there's also semantic ambiguity, because users reasonably expect short 
closures to perform auto-capturing:
 <?php
 $foo = 'foo';
 const Closure = static fn (array $bar): array => [$foo, $bar];
 var_dump((Closure)('bar'));
If this would be legal syntax: What would you expect to be printed?
Best regards 
Tim Düsterhus
Hi
Am 2024-10-30 05:25, schrieb Larry Garfield:
This seems like a good idea to me. My only real question is why we
need to forbid short-closures. I fully agree that capturing variables
for such functions doesn't work. What I don't understand is why that
precludes short-closures. Is it not possible to "just" say "there's
nothing to even capture in this context, don't try"? (There may be
technical reasons for that, but I do not know what they are and the RFC
doesn't say.)It would indeed require some special handling to disable the
auto-capturing in the code. This would be solvable of course, but
there's also semantic ambiguity, because users reasonably expect short
closures to perform auto-capturing:
Hi Tim,
So, why not allow capturing, since anyway the only place to capture are 
constants and static variables? 
And this way we could have short closures with auto-capture. 
If there are some technical reasons for not doing that, can we have written 
in the RFC?
Alex
Hi Tim,
So, why not allow capturing, since anyway the only place to capture are
constants and static variables?
And this way we could have short closures with auto-capture.
If there are some technical reasons for not doing that, can we
have written in the RFC?Alex
You don't need to capture constants or statics at all since they're all 
globally accessible (Well, except protected/private ones but that would 
require assuming a scope at compile time which is probably a bad idea)
Hi
Am 2024-10-30 09:31, schrieb Alexandru Pătrănescu:
So, why not allow capturing, since anyway the only place to capture are
constants and static variables?
Static variables already support arbitrary expressions since 
https://wiki.php.net/rfc/arbitrary_static_variable_initializers. The RFC 
initially mentioned static variables as a place where only const-expr 
would be accepted, but I removed that once I remembered about that other 
RFC.
And this way we could have short closures with auto-capture.
If there are some technical reasons for not doing that, can we have
written
in the RFC?
As stated in the RFC: This constraint is consistent with not supporting 
variables in constant expressions. Capturing a variable is effectively 
the same as reading a variable. It makes no sense to support one, but 
not the other.
Best regards 
Tim Düsterhus
Hi Tim
śr., 30 paź 2024 o 09:02 Tim Düsterhus tim@bastelstu.be napisał(a):
Hi
Am 2024-10-30 05:25, schrieb Larry Garfield:
This seems like a good idea to me. My only real question is why we
need to forbid short-closures. I fully agree that capturing variables
for such functions doesn't work. What I don't understand is why that
precludes short-closures. Is it not possible to "just" say "there's
nothing to even capture in this context, don't try"? (There may be
technical reasons for that, but I do not know what they are and the RFC
doesn't say.)It would indeed require some special handling to disable the
auto-capturing in the code. This would be solvable of course, but
there's also semantic ambiguity, because users reasonably expect short
closures to perform auto-capturing:<?php $foo = 'foo'; const Closure = static fn (array $bar): array => [$foo, $bar];
Personally, I'd expect short-closures be available as well, did you though 
about maybe reusing const token instead of static to make the 
information visually available for users?
I mean if PHP could parse const Closure = const fn (array $bar): array => [$foo, $bar]; and the documentation tells that const modifier prevent 
auto-capture in case of short-closures but also in case of normal closures 
to prevent use of use() clause that would be visible instantly in the code.
Consider this syntax as well:
<?php
$foo = '';
const Closure = const function (array $bar): array { return [$foo, $bar]; }
This way we could also forbid use of global-variables, use() clause and 
maybe even static $baz = 1 inside functions. So the closures better match 
constant semantics. WDYT?
The engine could create an instance of let's say \ConstClosure that would 
be a subtype of \Closure so it still can be used as a normal closure but 
not the other way. Effectively this would make these closures 
pure-functions if am not wrong here.
Overall I like the idea.
Cheers, 
Michał Marcin Brzuchalski
Hi
Am 2024-10-30 10:58, schrieb Michał Marcin Brzuchalski:
Personally, I'd expect short-closures be available as well, did you
though
about maybe reusingconsttoken instead ofstaticto make the
information visually available for users?
We do not believe the cost-benefit ratio is worth it to introduce new 
syntax and an entirely new language concept.
Best regards 
Tim Düsterhus
Hi
Am 2024-10-30 05:25, schrieb Larry Garfield:
This seems like a good idea to me. My only real question is why we
need to forbid short-closures. I fully agree that capturing variables
for such functions doesn't work. What I don't understand is why that
precludes short-closures. Is it not possible to "just" say "there's
nothing to even capture in this context, don't try"? (There may be
technical reasons for that, but I do not know what they are and the RFC
doesn't say.)It would indeed require some special handling to disable the
auto-capturing in the code. This would be solvable of course, but
there's also semantic ambiguity, because users reasonably expect short
closures to perform auto-capturing:<?php $foo = 'foo'; const Closure = static fn (array $bar): array => [$foo, $bar]; var_dump((Closure)('bar'));If this would be legal syntax: What would you expect to be printed?
Best regards
Tim Düsterhus
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. :-) Frankly I can't recall the last time I used a non-class constant period. :-)
That said, PHP consts are already a bit squishy, and auto-capture is always by value. That wouldn't give us a loophole?
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.)
--Larry Garfield
Hi
Am 2024-10-30 05:25, schrieb Larry Garfield:
This seems like a good idea to me. My only real question is why we
need to forbid short-closures. I fully agree that capturing variables
for such functions doesn't work. What I don't understand is why that
precludes short-closures. Is it not possible to "just" say "there's
nothing to even capture in this context, don't try"? (There may be
technical reasons for that, but I do not know what they are and the RFC
doesn't say.)It would indeed require some special handling to disable the
auto-capturing in the code. This would be solvable of course, but
there's also semantic ambiguity, because users reasonably expect short
closures to perform auto-capturing:<?php $foo = 'foo'; const Closure = static fn (array $bar): array => [$foo, $bar]; var_dump((Closure)('bar'));If this would be legal syntax: What would you expect to be printed?
Best regards
Tim DüsterhusHm. It would never occur to me to use a function for a non-class constant in the first place, so I don't know. :-) Frankly I can't recall the last time I used a non-class constant period. :-)
That said, PHP consts are already a bit squishy, and auto-capture is always by value. That wouldn't give us a loophole?
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.)
--Larry Garfield
To be honest, I thought it was a rhetorical question since the example is a runtime error (passing string to a function that takes an array), but on this note, we already know how it should behave: https://3v4l.org/RC6b3 because, as you said, it is captured by value. In theory, to support this, we would probably need “late binding constants” or constants that are executed just before any userland code is run. Similar to just emulating it yourself: https://3v4l.org/PMY4W
IMHO, the rules around constant expressions are not sound and feel arbitrary when you run into them. They are too easy to work around (though cumbersome) and the code still works, which seems to prove their arbitrary nature.
— Rob
To be honest, I thought it was a rhetorical question since the example is a runtime error (passing string to a function that takes an array), but on this note, we already know how it should behave: https://3v4l.org/RC6b3 because, as you said, it is captured by value. In theory, to support this, we would probably need “late binding constants” or constants that are executed just before any userland code is run. Similar to just emulating it yourself: https://3v4l.org/PMY4W
IMHO, the rules around constant expressions are not sound and feel arbitrary when you run into them. They are too easy to work around (though cumbersome) and the code still works, which seems to prove their arbitrary nature.
define()does not produce "true" constants and has always allowed to define constants at run-time where there is more information available.
const expressions have always been about determining and defining constants at compile time so they can be put in SHM. 
Which is why the const keyword exist. 
(and yes I know define() expression that are const expression are "true compile time constants")
Best regards, 
Gina P. Banyard
To be honest, I thought it was a rhetorical question since the example is a runtime error (passing string to a function that takes an array), but on this note, we already know how it should behave: https://3v4l.org/RC6b3 because, as you said, it is captured by value. In theory, to support this, we would probably need “late binding constants” or constants that are executed just before any userland code is run. Similar to just emulating it yourself: https://3v4l.org/PMY4W
IMHO, the rules around constant expressions are not sound and feel arbitrary when you run into them. They are too easy to work around (though cumbersome) and the code still works, which seems to prove their arbitrary nature.
define()does not produce "true" constants and has always allowed to define constants at run-time where there is more information available.const expressions have always been about determining and defining constants at compile time so they can be put in SHM.
Which is why the const keyword exist.
(and yes I knowdefine()expression that are const expression are "true compile time constants")Best regards,
Gina P. Banyard
Hey Gina,
I was mostly referring to the comment that we can't define the expected behavior, not necessarily what is a "true constant" or not.
— Rob
Hi
Am 2024-10-31 08:22, schrieb Rob Landers:
To be honest, I thought it was a rhetorical question since the example
is a runtime error (passing string to a function that takes an array),
That clearly was a typo / copy-paste mistake / whatever you would like 
to call it.
but on this note, we already know how it should behave:
https://3v4l.org/RC6b3 because, as you said, it is captured by value
The context was “can't we just pretend there’s nothing to capture in 
const-expr contexts”. The example was intended to point out how that 
will lead to semantic sadness.
In theory, to support this, we would probably need “late binding
constants” or constants that are executed just before any userland code
is run. Similar to just emulating it yourself: https://3v4l.org/PMY4W
Yes, but see also my new reply to Larry (and the reply to Alex).
Best regards 
Tim Düsterhus
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. :-) Frankly I can't
recall the last time I used a non-class constant period. :-)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?
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
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. :-) Frankly I can't
recall the last time I used a non-class constant period. :-)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?
Isn't this semantically equivalent to:
 function foo(
     string $bar,
     Closure $baz,
 ) {
     $baz = static fn () => $bar;
     var_dump($baz());
 }
 foo('captured');
— Rob
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
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. :-) Frankly I can't
recall the last time I used a non-class constant period. :-)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. :-)
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
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. :-)
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 Monday, 4 November 2024 at 20:32, Larry Garfield
larry@garfieldtech.com 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. :-)
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. :-)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
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: https://wiki.php.net/rfc/closures_in_const_expr
 - Implementation: https://github.com/php/php-src/pull/16458
 Best regards
Tim Düsterhus
Hi Tim,
This looks interesting, but I notice it says that the closure can access “private properties”:
This means that Closures in property default values may access private properties, methods, and class constants of the class where they are defined
I assume this means private static properties since it couldn’t be attached/bound to a specific instance? (Must be static?) 
— Rob
Hi
Am 2024-10-30 13:06, schrieb Rob Landers:
This looks interesting, but I notice it says that the closure can
access “private properties”:This means that Closures in property default values may access private
properties, methods, and class constants of the class where they are
definedI assume this means private static properties since it couldn’t be
attached/bound to a specific instance? (Must be static?)
No: All properties. An object of the class could be passed as a 
parameter to the Closure and then the Closure would be able to access 
private properties of that object.
See Zend/tests/closure_const_expr/property_initializer_scope_001.phpt 
in the PR for an example.
Best regards 
Tim Düsterhus
Hi
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: https://wiki.php.net/rfc/closures_in_const_expr
 - Implementation: https://github.com/php/php-src/pull/16458
 Best regards
Tim Düsterhus
I very much like this feature, a bit sad that it doesn't support First Class Callable syntax, but I understand the increased complexity this would add. 
Especially as supporting it would remove the last good reason to keep the callable type, which is to define a list of callables statically.
Best regards,
Gina P. Banyard
Hi
Am 2024-10-31 15:16, schrieb Gina P. Banyard:
I very much like this feature, a bit sad that it doesn't support First
Class Callable syntax, but I understand the increased complexity this
would add.
We hope to follow-up with a separate RFC for that, once we figured out 
the technical implementation and thus the necessary constraints (if any) 
that will need to be applied.
Best regards 
Tim Düsterhus
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: https://wiki.php.net/rfc/closures_in_const_expr
 - Implementation: https://github.com/php/php-src/pull/16458
 
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