RFC: Support for multi-line arrow functions

php.internals

Nuno Maduro

5 years ago
Hey internals, 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. As you may already know, PHP 7.4 has introduced one-liner arrow functions (aka short closures). Now, this pull request adds the possibility of those arrow functions to be multi-line. Just like in Javascript, or Hack. Let's see an example: ```php $users = [/** */]; $guestsIds = [/** */]; $repository = /** */; $guests = array_filter($users, fn ($user) => { $guest = $repository->findByUserId($user->id); return $guest !== null && in_array($guest->id, $guestsIds); }); ``` In short, the advantages are: ``` $values = array_filter($values, function ($value) use ($first, $second, $third) { // vs $values = array_filter($values, fn ($value) => { ``` - Multi-line arrow functions don't require the `use` keyword to be able to access data from the outer scope. - Also, in some cases, `fn (/** */) => {` is just shorter and simpler than `function (/** */) use (/** */) {`. This pull request has been very well received by the community on Github, and also on Twitter: https://twitter.com/enunomaduro/status/1311572174256340992. So, I am writing this email to get your feedback about having this feature in the core of PHP. Note that, if eventually, you think it is a good idea to create an RFC on this, I will need to get some RFC karma - my wiki account is nunomaduro. Regards, Nuno - https://twitter.com/enunomaduro.

Marco Pivetta

5 years ago
Hey Nuno, <http://ocramius.github.com/> On Sat, Oct 3, 2020 at 11:09 PM Nuno Maduro <enunomaduro@gmail.com> wrote:
> Hey internals, > > > 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. > > > As you may already know, PHP 7.4 has introduced one-liner arrow functions > (aka short closures). Now, this pull request adds the possibility of those > arrow functions to be multi-line. Just like in Javascript, or Hack. Let's > see an example: > > > ```php > > $users = [/** */]; > > $guestsIds = [/** */]; > > $repository = /** */; > > > $guests = array_filter($users, fn ($user) => { > > $guest = $repository->findByUserId($user->id); > > > return $guest !== null && in_array($guest->id, $guestsIds); > > }); > > ``` > > > In short, the advantages are: > > ``` > > $values = array_filter($values, function ($value) use ($first, $second, > $third) { > > // vs > > $values = array_filter($values, fn ($value) => { > > ``` > > > - Multi-line arrow functions don't require the `use` keyword to be able to > access data from the outer scope. > > - Also, in some cases, `fn (/** */) => {` is just shorter and simpler than > `function (/** */) use (/** */) {`. > > > This pull request has been very well received by the community on Github, > and also on Twitter: > https://twitter.com/enunomaduro/status/1311572174256340992. > > > So, I am writing this email to get your feedback about having this feature > in the core of PHP. Note that, if eventually, you think it is a good idea > to create an RFC on this, I will need to get some RFC karma - my wiki > account is nunomaduro. >
Overall, seems like unnecessary added syntax for something that already works just fine. Pros: * unclear Cons: * more scope leak * +1 way to do what we already do just fine * more lexer/ast complexity for downstream projects If you want to make an RFC about it, it really needs a compelling "pros" section, because there aren't any, so far :-\ Greets, Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/

Benjamin Morel

5 years ago
> > If you want to make an RFC about it, it really needs a compelling "pros" > section, because there aren't any, so far :-\
I don't agree. There is one pro, and not a small one IMO if you use closures a lot: skip use(). Since arrow functions have been introduced, I've several times naturally tried to open braces, only to remember that it's not supported. I love fn() for the fact that it doesn't require the use() statement, and would love to see it extended to more than a simple expression. — Benjamin

Deleu

5 years ago
Hi Nuno, thank you very much for kicking this off! The current state of short closure is very limited and I believe it was a stepping stone to get to this one. This time there's no drawback of making fn a reserved keyword since it already is. This change will lead to better consistency and it will fill in the expectation gap when using short closure in PHP. Best of luck for us on this RFC. On Sat, Oct 3, 2020, 23:09 Nuno Maduro <enunomaduro@gmail.com> wrote:

Rowan Collins

5 years ago
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]

Mike Schinkel

5 years ago
> On Oct 4, 2020, at 4:08 PM, Rowan Tommins <rowan.collins@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

Andreas Leathley

5 years ago
On 04.10.20 22:08, Rowan Tommins wrote:
> 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.
Something like "use (*)" seems like a great enhancement to me. I often use a wrapper function for SQL transactions, something like: ```php public function update(int $numberId, int $addressId, bool $isMainNumber = false): void {     $this->transaction->run(function () use ($numberId, $addressId, $isMainNumber): void {       // Do all SQL queries for the update     }); } ``` In these cases there is a lot of redundancy because of having to import the variables, and if a variable is added, it has to be added in two places in a slightly different way. The following would be much nicer: ```php public function update(int $numberId, int $addressId, bool $isMainNumber = false): void {     $this->transaction->run(function () use (*): void {       // Do all SQL queries for the update     }); } ``` This would also increase code readability.

Michael Voříšek - ČVUT FEL

5 years ago
Yes, "use (*)" is perfect! With kind regards / Mit freundlichen Grüßen / S přátelským pozdravem, Michael Voříšek On 5 Oct 2020 11:57, Andreas Leathley wrote:

Lynn

5 years ago
On Mon, Oct 5, 2020 at 12:00 PM Michael Voříšek - ČVUT FEL < vorismi3@fel.cvut.cz> wrote:
> Yes, "use (*)" is perfect! > > With kind regards / Mit freundlichen Grüßen / S přátelským pozdravem, > > Michael Voříšek > > On 5 Oct 2020 11:57, Andreas Leathley wrote: > > > On 04.10.20 22:08, Rowan Tommins wrote: > > > >> 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. > > > > Something like "use (*)" seems like a great enhancement to me. I often > > use a wrapper function for SQL transactions, something like: > > > > ```php > > public function update(int $numberId, int $addressId, bool $isMainNumber > > = false): void > > { > > $this->transaction->run(function () use ($numberId, $addressId, > > $isMainNumber): void { > > // Do all SQL queries for the update > > }); > > } > > > > ``` > > > > In these cases there is a lot of redundancy because of having to import > > the variables, and if a variable is added, it has to be added in two > > places in a slightly different way. The following would be much nicer: > > > > ```php > > public function update(int $numberId, int $addressId, bool $isMainNumber > > = false): void > > { > > $this->transaction->run(function () use (*): void { > > // Do all SQL queries for the update > > }); > > } > > > > ``` > > > > This would also increase code readability. > > > > -- > > PHP Internals - PHP Runtime Development Mailing List > > To unsubscribe, visit: https://www.php.net/unsub.php
How should php deal with the scenario where you want to `use` everything and have one variable by reference? ``` function () use (*, &$butNotThisOne) {}; ```

Andreas Leathley

5 years ago
On 05.10.20 12:08, Lynn wrote:
> How should php deal with the scenario where you want to `use` everything > and have one variable by reference? > > ``` > function () use (*, &$butNotThisOne) {}; > ```
The easiest would be to only allow "use (*)" with no references or additional syntax. "use (*)" would only copy all local variables into the closure, no references. Personally I have never used references with "use", I think it is much more niche compared to the regular copying, and there is still the explicit (current) syntax to do references.

Markus Fischer

5 years ago
On 05.10.20 12:24, Andreas Leathley wrote:
> On 05.10.20 12:08, Lynn wrote: >> How should php deal with the scenario where you want to `use` everything >> and have one variable by reference? >> >> ``` >> function () use (*, &$butNotThisOne) {}; >> ``` > > The easiest would be to only allow "use (*)" with no references or > additional syntax. "use (*)" would only copy all local variables into > the closure, no references. Personally I have never used references with > "use", I think it is much more niche compared to the regular copying, > and there is still the explicit (current) syntax to do references.
FTR, short arrow function implicitly only support "by value" bindings, I just checked the docs. I do use by ref with closures btw. - Markus

Sara Golemon

5 years ago
On Mon, Oct 5, 2020 at 5:09 AM Lynn <kjarli@gmail.com> wrote:
> How should php deal with the scenario where you want to `use` everything > and have one variable by reference? > > ``` > function () use (*, &$butNotThisOne) {}; > ```
I would take a page out of C++'s book here. In C++ a closure is (some of these bits can be omitted for brevity, but I'm not going to describe those here as it's orthogonal to the topic): [capture, vars](type argName) -> returnType { statements; return retVal; } Looking specifically at the capture vars section (in the example we are capturing two variables, one named 'capture', one named 'vars'), there are wildcards available as well: [=](bool bval) -> void {} The equal sign (as above) captures all variables used in the closure by value. Similarly, the ampersand `[&](int ival) -> void {}` captures all variables used by reference. Exceptions can be added to that list just as you suggested, so: [=,&foo](double dval) -> double { return foo + dval; } Or [&,foo]() -> void { doSomething(foo); } I think we could apply the same in PHP terms: function(float $dval) use ($, &$foo) { return $foo + $dval; }; function() use (&$, $foo) { doSomething($foo); }; Plus or minor for parser convenience. -Sara

Deleu

5 years ago
To me that seems like a great argument in favour of the proposal. If you'll want all variables to be imported (which in this case makes completely sense), then `fn() {}` or `fn() => {}` is much less verbose and inline with the mentality to reach for short closures. We reach for short closures to avoid `use()` and convey that the outer process is intertwined with the inner process. `fn()` allows to strengthen the concept that there's no real separation between running SQL stuff in a `callable` that wraps a database transaction. On Mon, Oct 5, 2020 at 11:57 AM Andreas Leathley <a.leathley@gmx.net> wrote:
> On 04.10.20 22:08, Rowan Tommins wrote: > > 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. > > Something like "use (*)" seems like a great enhancement to me. I often > use a wrapper function for SQL transactions, something like: > > ```php > public function update(int $numberId, int $addressId, bool $isMainNumber > = false): void > { > $this->transaction->run(function () use ($numberId, $addressId, > $isMainNumber): void { > // Do all SQL queries for the update > }); > } > > ``` > > In these cases there is a lot of redundancy because of having to import > the variables, and if a variable is added, it has to be added in two > places in a slightly different way. The following would be much nicer: > > ```php > public function update(int $numberId, int $addressId, bool $isMainNumber > = false): void > { > $this->transaction->run(function () use (*): void { > // Do all SQL queries for the update > }); > } > > ``` > > This would also increase code readability. > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > >
-- Marco Aurélio Deleu

Andreas Leathley

5 years ago
On 05.10.20 12:15, Deleu wrote:
> To me that seems like a great argument in favour of the proposal. If you'll > want all variables to be imported (which in this case makes completely > sense), then `fn() {}` or `fn() => {}` is much less verbose and inline with > the mentality to reach for short closures. We reach for short closures to > avoid `use()` and convey that the outer process is intertwined with the > inner process. `fn()` allows to strengthen the concept that there's no real > separation between running SQL stuff in a `callable` that wraps a database > transaction.
Not necessarily: the arrow functions were specifically implemented for very short anonymous functions with a return value. Making them more and more like the existing "function" syntax would lead to having two different ways of defining anonymous functions that mainly differentiate themselves by including the parent scope by default or not. I like the "function () use (*)" suggestion because it is explicit and opt-in. A shorter syntax like "fn () {}" is less clear, and it could lead to many people always using fn just because it is faster to write (and less to think about), which then could lead to unintended side effects because variables are being copied from the parent scope each time. When you see a usage of "fn () {}" while reading code you would not know if the person used it because it was faster to write, or if the parent scope variables are really needed.

Brent

5 years ago
Hi internals The reason multi-line short closures are so valued by userland devs is because they are shorter to write and prettier to read. While some of us might not agree on the definition of "prettier to read", it was one of the key arguments for adding short closures in the first place:
> Anonymous functions in PHP can be quite verbose, even when they only perform a simple operation. Partly this is due to a large amount of syntactic boilerplate, and partly due to the need to manually import used variables. This makes code using simple closures hard to read and understand. This RFC proposes a more concise syntax for this pattern. [1] >
We can have the discussion again on whether we like short closures or not, but it turned out most of internals _and_ userland devs do — based on the vote count in the sigle line RFC and the reaction on Nuno's PR, as well as my experience from an OSS maintainer point of view. Furthermore, the `use(*)` syntax misses the point of this proposal: it's not about being able to use all variables from the outer scope, it's about a clean syntax that's as short as possible — even when you personally disagree that it is. I've made the same argument before on this list: it's clear that the PHP community _wants_ these changes: named arguments, property promotions, short closures, … these are all features that aren't _necessary_, still they are great features of a modern-day language. I also want to quote from Larry Garfields book on thinking functionally in PHP [2], to demonstrate the signicant impact short closures already had today:
> “Combined with PHP’s overall clunky syntax for doing functional-esque code, I generally didn’t go further than “pure functions are your friend,” either in my own code or what I explained to others. > > That is, until PHP 7.4. > > PHP 7.4’s introduction of short lambdas is, as we’ll see in this book, a game-changer. While it doesn’t make anything new possible, it makes a lot of things suddenly practical. That makes all the difference, so I decided it was time to buckle down and really dig into functional programming.”
Larry continues to write a whole book about functional programming in PHP, and short closures play a significant role. So I hope to see more input on Nuno's PR from a techinical point of view: what's missing, what's needed to get this to the RFC phase, … and not only discussions about what syntax we like or not, or whether there are other ways to solve the same problem. Please provide Nuno with actionable feedback. Kind regards Brent [1] https://wiki.php.net/rfc/arrow_functions_v2 <https://wiki.php.net/rfc/arrow_functions_v2> [2] https://leanpub.com/thinking-functionally-in-php <https://leanpub.com/thinking-functionally-in-php>

Girgias

5 years ago
First, can you please bottom-post and not top-post. On Tue, 6 Oct 2020 at 09:53, Brent Roose <brendt@stitcher.io> wrote:
> Hi internals > > The reason multi-line short closures are so valued by userland devs is > because they are shorter to write and prettier to read. While some of us > might not agree on the definition of "prettier to read", it was one of the > key arguments for adding short closures in the first place: > > > Anonymous functions in PHP can be quite verbose, even when they only > perform a simple operation. Partly this is due to a large amount of > syntactic boilerplate, and partly due to the need to manually import used > variables. This makes code using simple closures hard to read and > understand. This RFC proposes a more concise syntax for this pattern. [1] > > > > We can have the discussion again on whether we like short closures or not, > but it turned out most of internals _and_ userland devs do — based on the > vote count in the sigle line RFC and the reaction on Nuno's PR, as well as > my experience from an OSS maintainer point of view. >
I didn't know we were meant to do code golfing with production code, might have missed a memo somewhere.
> Furthermore, the `use(*)` syntax misses the point of this proposal: it's > not about being able to use all variables from the outer scope,
If it's not about being able to use all variables (or even just one that is irrelevant) from the outer scope, then what is the point? Saving 6 characters by only writing fn() {} instead of function {}?
> it's about a clean syntax that's as short as possible — even when you > personally disagree that it is. I've made the same argument before on this > list: it's clear that the PHP community _wants_ these changes:
Wanting something isn't an argument. Looking at what part of the community wants, we should be using @ for attributes. named arguments, property promotions, short closures, … these are all
> features that aren't _necessary_, still they are great features of a > modern-day language. >
Obviously nothing is necessary, we could write assembler style with only goto statements. I also want to quote from Larry Garfields book on thinking functionally in
> PHP [2], to demonstrate the signicant impact short closures already had > today: > > > “Combined with PHP’s overall clunky syntax for doing functional-esque > code, I generally didn’t go further than “pure functions are your friend,” > either in my own code or what I explained to others. > > > > That is, until PHP 7.4. > > > > PHP 7.4’s introduction of short lambdas is, as we’ll see in this book, a > game-changer. While it doesn’t make anything new possible, it makes a lot > of things suddenly practical. That makes all the difference, so I decided > it was time to buckle down and really dig into functional programming.” > > Larry continues to write a whole book about functional programming in PHP, > and short closures play a significant role. >
Finally a resemblance of an actual argument.
> So I hope to see more input on Nuno's PR from a techinical point of view: > what's missing, what's needed to get this to the RFC phase, … and not only > discussions about what syntax we like or not, or whether there are other > ways to solve the same problem. Please provide Nuno with actionable > feedback. > > Kind regards > Brent > > [1] https://wiki.php.net/rfc/arrow_functions_v2 < > https://wiki.php.net/rfc/arrow_functions_v2> > [2] https://leanpub.com/thinking-functionally-in-php < > https://leanpub.com/thinking-functionally-in-php> >
Jokes aside, the actionable feedback is to argue *why* auto capture of the outer scope should be added to the language as "it is very not PHP" a direct quote from Rasmus from his talk "25 years of PHP" [1] and from the same section one of the reasons why people don't mind the current single line expression form is because it doesn't look like a new scope. As Rowan said in his analysis changing this specific behaviour of scope being able to "leak" into another one needs a lot of justification, the current short closure syntax doesn't even use braces {} which are the de facto signal in PHP that you are entering in a new scope. Going back to the `use(*)` syntax: the reason why people propose this extension (which is not mutually exclusive with adding support for fn {} without outer scope capture, albeit strange) is that it is more in PHP traditional design philosophy. You can argue against this syntax and in favour of Nuno's, but again it is NOT missing the point. Moreover, Larry has also made a PR which extends short closures [2] in a way I personally find way more appealing. Regards George P. Banyard [1] https://youtu.be/Qa_xVjTiOUw?t=1895 [2] https://github.com/php/php-src/pull/6221

Girgias

5 years ago
On Tue, 6 Oct 2020 at 11:28, G. P. B. <george.banyard@gmail.com> wrote:
> First, can you please bottom-post and not top-post. > > On Tue, 6 Oct 2020 at 09:53, Brent Roose <brendt@stitcher.io> wrote: > >> Hi internals >> >> The reason multi-line short closures are so valued by userland devs is >> because they are shorter to write and prettier to read. While some of us >> might not agree on the definition of "prettier to read", it was one of the >> key arguments for adding short closures in the first place: >> >> > Anonymous functions in PHP can be quite verbose, even when they only >> perform a simple operation. Partly this is due to a large amount of >> syntactic boilerplate, and partly due to the need to manually import used >> variables. This makes code using simple closures hard to read and >> understand. This RFC proposes a more concise syntax for this pattern. [1] >> > >> >> We can have the discussion again on whether we like short closures or >> not, but it turned out most of internals _and_ userland devs do — based on >> the vote count in the sigle line RFC and the reaction on Nuno's PR, as well >> as my experience from an OSS maintainer point of view. >> > > I didn't know we were meant to do code golfing with production code, might > have missed a memo somewhere. > > >> Furthermore, the `use(*)` syntax misses the point of this proposal: it's >> not about being able to use all variables from the outer scope, > > > If it's not about being able to use all variables (or even just one that > is irrelevant) from the outer scope, then what is the point? > Saving 6 characters by only writing fn() {} instead of function {}? > > >> it's about a clean syntax that's as short as possible — even when you >> personally disagree that it is. I've made the same argument before on this >> list: it's clear that the PHP community _wants_ these changes: > > > Wanting something isn't an argument. Looking at what part of the community > wants, we should be using @ for attributes. > > named arguments, property promotions, short closures, … these are all >> features that aren't _necessary_, still they are great features of a >> modern-day language. >> > > Obviously nothing is necessary, we could write assembler style with only > goto statements. > > I also want to quote from Larry Garfields book on thinking functionally in >> PHP [2], to demonstrate the signicant impact short closures already had >> today: >> >> > “Combined with PHP’s overall clunky syntax for doing functional-esque >> code, I generally didn’t go further than “pure functions are your friend,” >> either in my own code or what I explained to others. >> > >> > That is, until PHP 7.4. >> > >> > PHP 7.4’s introduction of short lambdas is, as we’ll see in this book, >> a game-changer. While it doesn’t make anything new possible, it makes a lot >> of things suddenly practical. That makes all the difference, so I decided >> it was time to buckle down and really dig into functional programming.” >> >> Larry continues to write a whole book about functional programming in >> PHP, and short closures play a significant role. >> > > Finally a resemblance of an actual argument. > > >> So I hope to see more input on Nuno's PR from a techinical point of view: >> what's missing, what's needed to get this to the RFC phase, … and not only >> discussions about what syntax we like or not, or whether there are other >> ways to solve the same problem. Please provide Nuno with actionable >> feedback. >> >> Kind regards >> Brent >> >> [1] https://wiki.php.net/rfc/arrow_functions_v2 < >> https://wiki.php.net/rfc/arrow_functions_v2> >> [2] https://leanpub.com/thinking-functionally-in-php < >> https://leanpub.com/thinking-functionally-in-php> >> > > Jokes aside, the actionable feedback is to argue *why* auto capture of the > outer scope should be added to the > language as "it is very not PHP" a direct quote from Rasmus from his talk > "25 years of PHP" [1] and from the > same section one of the reasons why people don't mind the current single > line expression form is because it > doesn't look like a new scope. > As Rowan said in his analysis changing this specific behaviour of scope > being able to "leak" into another one > needs a lot of justification, the current short closure syntax doesn't > even use braces {} which are the de facto > signal in PHP that you are entering in a new scope. > > Going back to the `use(*)` syntax: the reason why people propose this > extension (which is not mutually exclusive > with adding support for fn {} without outer scope capture, albeit strange) > is that it is more in PHP traditional design > philosophy. You can argue against this syntax and in favour of Nuno's, but > again it is NOT missing the point. > > Moreover, Larry has also made a PR which extends short closures [2] in a > way I personally find way more appealing. > > Regards > > George P. Banyard > > [1] https://youtu.be/Qa_xVjTiOUw?t=1895 > [2] https://github.com/php/php-src/pull/6221 >
Ressending to the list as I seem to have messed up my reply.

Rowan Collins

5 years ago
Hi Brent, On 06/10/2020 09:53, Brent Roose wrote:
> We can have the discussion again on whether we like short closures or not, but it turned out most of internals_and_ userland devs do — based on the vote count in the sigle line RFC and the reaction on Nuno's PR, as well as my experience from an OSS maintainer point of view.
I don't know how much of the earlier discussion you saw at the time, or have looked back at, but it's worth noting that the arrow functions RFC which passed voting came after several long mailing list threads, and several complete rewrites of the proposal, at least one of which went to vote and was declined. The lack of multi-statement arrow functions wasn't an accident, it was part of how consensus was achieved. That doesn't mean we can't discuss multi-statement arrow functions now - they were listed as "Future Scope", and we are now in that Future. :) However, it does mean a new RFC doing so needs to set out its own justification, not assume that everyone who voted Yes on the previous RFC will vote Yes on this one as well.
> Furthermore, the `use(*)` syntax misses the point of this proposal: it's not about being able to use all variables from the outer scope, it's about a clean syntax that's as short as possible
Rather than "missing the point", I think it's a case of different people wanting different things from arrow functions. In one of the previous discussions [1] I suggested three perspectives of what people wanted them for: (a) an improved syntax for declaring closures (in the way that [$foo, $bar] is a shorter syntax for array($foo, $bar)) (b) improved *semantics* for declaring closures, where you don't have to list the variables to be captured (c) a new kind of closure designed for specific use cases, with its own syntax and semantics Those who wanted (c) are generally happy with what we have, with perhaps an interest in making more statements available as expressions, such as throw [2] and match [3]. Those who wanted (b) generally see the arrow syntax as a means to an end, and are open to alternative syntaxes that achieve the relevant semantics, such as use(*). Those who wanted (a) may see things the other way around: the short syntax is the goal, and auto-capture and implicit return are just the ways to achieve that goal. There's probably no way to please all three groups (and the various combinations and nuances I've over-simplified), but it's useful to acknowledge the existence of those who want *some* of what you want, but don't agree on all of it. I look forward to seeing the RFC if Nuno decides to proceed with it, because I am much more interested in how it will affect *users* of PHP than the technical details visible in the PR. [1] https://externals.io/message/98045#98209 [2] https://wiki.php.net/rfc/throw_expression [3] https://wiki.php.net/rfc/match_expression_v2 Regards,
-- Rowan Tommins (né Collins) [IMSoP]