> On Oct 4, 2020, at 4:08 PM, Rowan Tommins <rowan.coll...@gmail.com> wrote: > > 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.
Good analysis. I agree #2 is the strongest motivator for some kind of improvement / change. In at least one prior language I have used there was no distinction between local scope inside and outside of a closure, so when I first realized I had to use the "use" clause to make variable visible it was surprising to me. In PHP I find this requirement rather annoying in the majority of cases. My functions and closures tend to be short and the number of local variables I use — both inside and outside the closure —tend to be small so I don't have a problem with confusing scope. Having to explicitly name the variables in a use statement feels like unfortunate overkill. But I do get your point. OTOH, when inherited local variables are NOT modified within the closure, does that actually cause a problem? (honest question) If not — and please check my logic — I would suggest PHP 8.1+ could allow closures to IMPLICITLY inherit local variables, but ONLY for variables that are READ, not for variables that are written. Given this approach, variables would still need to be declared when variables need to be written, e.g. "use ( &$foo )" Although this relaxing of requirement to declare variables could theoretically break existing code, only code where variables are read inside closures before they are assigned with the same name as variables assigned outside the closure would "break", and those cases are flagged as a warning when error reporting is on. For me, that would probably cover 90%+ of the use cases my the requirement to employ the "use" statement feels like overkill. -Mike -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php