Hi Nuno,

On 03/10/2020 22:09, Nuno Maduro wrote:
A few days ago I opened a pull request that adds support for multi-line
arrow functions in PHP: https://github.com/php/php-src/pull/6246.


Welcome to the list. Firstly, it's probably worth having a look in the mailing list archives for prior discussions on this, as it was definitely discussed during the earlier short closures RFCs (the successful one that gave us fn()=>expr, and a couple of earlier attempts with different syntax and features).


Secondly, I'd like to point out that "short closures" actually have three fundamental features:

1. They have an implicit "return", making them ideal for single expressions rather than blocks of procedural code. 2. They automatically capture all variables in scope, rather than having to import them with "use". 3. They are shorter than normal closure declarations, both because of the above two features, and because "fn" is slightly shorter than "function".

I think it's worth making clear which of those three features we are hoping to retain by combining arrow functions with blocks.


Feature 1 doesn't extend to code blocks in an obvious way. In some languages, every statement is a valid expression, so you can place an implicit "return" before the last statement of a block. In PHP, that's not the case, so e.g. { $x = 'Hello World; echo $x; } cannot be converted to { $x = 'Hello World; return echo $x; }. Alternatives include converting to { $x = 'Hello World; echo $x; return null; } or requiring all closure blocks to end with a valid expression.


Feature 2 is probably the one most people actually want extended, but in my opinion is also the part most in need of justification, because it changes the language quite fundamentally.

There are currently very few places in PHP where the scope of a variable is not to the current function: properties of the current object must be accessed via $this, and class properties via self:: or similar; globals must be imported via a "global" statement, statics via a "static" statement, and closed-over values via the "use" keyword. (The main exception to this rule is the half-dozen built-in "superglobals"; I think there are a few more obscure cases.)

In a single-expression closure, as currently allowed, there is limited possibility for ambiguous scope. Extending this to function bodies of any size leads to much more risk of complexity and confusion.

If you want to capture variables $a, $b, and $c, but have local variables $x, $y, and $z, you would currently write this:

$f = function() use ($a, $b, $c) {
    // $x, $y, $z must be local, because not imported
}

If we added an opt-in syntax for "capture everything", we might instead write this:

$f = function() use (*) {
     $x = $y = $z = null;
}

Without re-initialising all local variables, we would no longer be able to know if they were actually local without looking at the surrounding scope for a value that might be captured. I am unconvinced by this trade-off of opt-out instead of opt-in.

One use case I've seen proposed is closures which capture a large number of variables; I would be interested to see an example where this is the case and is not a "code smell" in the same way as requiring a large number of parameters.


In the above example I deliberately did not use the "fn()=>" syntax, because I believe this is really orthogonal to the other features - my impression is that actual expression length (feature 3 above) is more of a pleasant side-effect than a top priority for most people.

I would personally prefer the "fn()=>" syntax to carry on meaning "this is an expression elevated to function status", and have some other syntax for a full closure that uses auto-capturing scope rules if that feature is indeed needed.


Regards,

--
Rowan Tommins (né Collins)
[IMSoP]

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php

Reply via email to