Re: [RFC] Short Closures 2, aka auto-capture take 3
| From: | Mike Schinkel | Date: | Tue, 14 Jun 2022 14:28:21 +0000 |
| Subject: | Re: [RFC] Short Closures 2, aka auto-capture take 3 | ||
| References: | 1 2 3 4 5 6 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-117944@lists.php.net to get a copy of this message | ||
> On Jun 13, 2022, at 1:39 PM, Rowan Tommins <rowan.collins@gmail.com> wrote:
>
> On 13/06/2022 14:52, Larry Garfield wrote:
>>> That's one perspective. The other perspective is that the proposal is to
>>> extend closure syntax to support automatic capture.
>> As noted before, this is a distinction without a difference.
>
>
> It's a difference in focus which is very evident in some of the comments on this thread.
> For instance, Arnaud assumed that adding "use(*)" would require a change to arrow
> functions, whereas that never even occurred to me, because we're looking at the feature through
> a different lens.
>
>
>>> By the way, the current RFC implies you could write this:
>>>
>>> fn() use (&$myRef, $a) { $myRef = $a * $b; }
>> The RFC already covers that. $b will be auto-captured by value from scope if it exists.
>> See the "Explicit capture" section and its example.
>
>
> So it does. I find that extremely confusing; I think it would be clearer to error for that
> case, changing the proposal to:
>
> > Short Closures support explicit by-reference capture with the |use| keyword. Combining a
> > short closure with explicit by-value capture produces an error.
>
> And the example to:
>
> $a = 1;
> fn () use (&$b) {
> return $a + $b; // $a is auto-captured by value
> // $b is explicitly captured by reference
> }
>
>
> Clearer syntax for this has been cited previously as an advantage of use(*) or use(...):
>
> $a = 1;
> function () use (&$b, ...) { // read as "use $b by reference, everything else by
> value"
> return $a + $b;
> }
>
>
>> 1. Auto-capture could still over-capture without people realizing it. Whether this is
>> actually an issue in practice (or would be) is hard to say with certainty; I'm not sure if
>> it's possible to make an educated guess based on a top-1000 analysis, so we're all trying
>> to predict the future.
>
>
> I tried to make very explicit what I was and was not disputing:
>
> > Whether the risk of these side effects is a big problem is up for debate, but it's
> > wrong to suggest they don't exist.
>
> The RFC seems to be implying that the implementation removes the side effects, but it does not,
> it is users paying attention to their code which will remove the side effects.
>
>
>> Arguably it's less of an issue only because short-closures are, well, short, so less
>> likely to reuse variables unintentionally.
>
>
> Our current short closures aren't just a single *statement*, they're a single
> *expression*, and that's a really significant difference, because it means to all intents and
> purposes *they have no local scope*. (You can create and use a local variable within one expression,
> but it requires the kind of twisted code that only happens in code golf.)
>
> If there are no local variables, there is nothing to be accidentally captured. That's why
> the current implementation doesn't bother optimising which variables it captures - it's
> pretty safe to assume that *all* variables in the expression are either parameters or captured.
>
>
>> 2. The syntactic indicator that "auto capture will happen". The RFC says
>> "fn". You're recommending "use(*)". However, changing the indicator
>> syntax would do nothing to improve point 1.
>
>
> The reason I think it would be better is because it is a more *intentional* syntax: the author
> of the code is more likely to think "I'm using an auto-capture closure, rather than an
> explicit-capture closure, what effect will that have?" and readers of the code are more likely
> to think "hm, this is using auto-capture, I wonder which variables are local, and which are
> captured?"
>
> Of course they can still guess wrong, but I don't think "fn" vs
> "function" is a strong enough clue.
"Strong enough" is an opinion, and it seems all who have commented have differing ones of
those.
But maybe a memory device would help address (some of?) your concerns:
- fn() — It is SHORT and implicit. Short is CONVENIENT. Thus Short auto-captures variables because
that is the most Convenient thing to do.
- function() — It is LONG. Long is more EXPLICIT. Thus Long requires Explicitly declaring
variables, which is also more rigorous and robust.
Or for the TL;DR crowd:
- fn() => SHORT => CONVENIENT => Auto-captures
- function() => LONG => EXPLICIT => Requires declaration
Hope this helps. #fwiw
-Mike