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.)
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:
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.
On Wed, Oct 30, 2024 at 10:03 AM Tim Düsterhus <tim@bastelstu.be> wrote:
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?
ś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:
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
On Wed, Oct 30, 2024, at 3:01 AM, Tim Düsterhus wrote:
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:
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.)
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
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.
On Thursday, 31 October 2024 at 07:22, Rob Landers <rob@bottled.codes> wrote:
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: Online PHP editor | output for 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: Online PHP editor | output for 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")
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.
On Thu, Oct 31, 2024, at 15:12, Gina P. Banyard wrote:
On Thursday, 31 October 2024 at 07:22, Rob Landers rob@bottled.codes wrote:
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
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.
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?)
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.
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 PHP: 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.
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 reusing `const` token instead of `static` to 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.
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?
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.
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: Online PHP editor | output for PMY4W
Yes, but see also my new reply to Larry (and the reply to Alex).
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.