On 30/06/2022 14:25, Dan Ackroyd wrote:
With automatic capturing of variables, for the code example I gave the
user would want the variable to be captured, and to them it looks like
it should be, but because of an optimization it is not.
Please look again at the detailed explanation I gave, and the examples
that Guilliam posted. Your example can *only* work if the variable is
captured by reference, because it requires the statement *inside* the
closure to have an effect on a variable *outside* the closure. No
version of auto-capture has ever proposed capturing by reference.
If instead of $some_resource = null; you wrote
$some_container->some_resource = null; then that would have an effect on
the object, but the "optimisation" would be irrelevant because the use
of $some_container itself is not an assignment.
As I said, I think that problem is a lot easier to explain "either use
long closures or change your variable name if you don't want it
captured." than trying to explain "yes, the variable is referenced
inside the closure, but it's not captured because you aren't reading
from it".
Right now, assigning (or unsetting) a variable is the *only* way to
force it to be local. That's why I said I would be more likely to
support this feature alongside a "var" or "let" keyword to make such
variables explicit. Not being able to have local variables *at all*
other than by very careful variable naming is a terrible idea.
Just to re-iterate, here's your new example with explicit capture, to
demonstrate that the closure *does not and cannot free the resource*:
https://3v4l.org/WrTb5
class ResourceType
{
public function __destruct() {
echo "Resource is released.\n";
}
}
function get_callback()
{
$some_resource = new ResourceType();
$fn = function() use ($some_resource) {
// this line does nothing
// it overwrites a local variable which is never read
// next time the closure runs, it will start again as the captured value
$some_resource = null;
};
return $fn;
}
$fn = get_callback();
echo "Before callback\n";
$fn();
echo "After callback\n";
unset($some_resource);
echo "After destroying outer var\n";
// the captured reference is still live here, no matter how many times we call
$fn()
// only destroying the closure frees it
unset($fn);
echo "After destroying closure\n";
One way of thinking of it is that assignments inside a closure are
assignments to a local variable, which "shadow" any captured variable
with the same name. If all you do with a variable is shadow it, then it
is illogical to consider it "used" in that function.
On 30/06/2022 15:18, Robert Landers wrote:
Are
optimizations going to be applied to single-line arrow functions (I
didn't see that in the RFC, but I admittedly didn't look that hard and
I vaguely remember reading something about it in one of these
threads)?
I would expect so, yes. It could be considered a bug that the arrow
function implementation currently "over-captures" variables, and it only
wasn't a higher priority in Nikita's RFC because it is extremely rare
that a single expression closure would have any local variables. Indeed,
that lack of local scope is one of the big reasons why I and others
supported that RFC, because it avoids all the confusion evident in
today's messages.
Regards,
--
Rowan Tommins
[IMSoP]
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php