Re: [PHP-DEV] Re: [RFC] Arrow functions / short closures

This is only part of a thread. view whole thread
  104725
March 14, 2019 19:41 theodorejb@outlook.com (Theodore Brown)
On Thu, March 14, 2019 10:41 AM Nikita Popov ppv@gmail.com> wrote:

> On Wed, Mar 13, 2019 at 4:56 PM Nikita Popov ppv@gmail.com> wrote: > > > Hi internals, > > > > Motivated by the recent list comprehensions RFC, I think it's time we took > > another look at short closures: > > > > https://wiki.php.net/rfc/arrow_functions_v2 > > As a small update, I've implemented a proof of concept that uses the ($x) > ==> $x * $multiplier syntax (or $x ==> $x * $multiplier for short) in > https://github.com/php/php-src/pull/3945. As mentioned in the RFC, this > requires scanahead in the lexer. > > This syntax is in principle still on the table, though personally I prefer > fn($x, $y) => $x * $y over ($x, $y) ==> $x * $y. The main redeeming quality > of ==> is that it supports the paren-less variant $x ==> $x. Taking into > account the lexer hackery it requires (and which will also be required in > any 3rd party tooling), this would not be my preferred choice. >
I agree that the nicest thing about this syntax is the ability to save an additional 3 characters of boilerplate for the common use case of single-parameter arrow functions. However, I'm also not a fan of adding complex code hacks to make the syntax work. One alternative that doesn't seem to have had much discussion on list is the `\($x) => $x * $y` lambda syntax. This would also allow parentheses to be omitted for single parameters, making it just as terse as the ==> syntax without the need for any lexer hackery. Here's how the examples from the RFC would look: ```php function array_values_from_keys($arr, $keys) { return array_map(\$x => $arr[$x], $keys); } $extended = \$c => $callable($factory($c), $c); $this->existingSchemaPaths = array_filter($paths, \$v => in_array($v, $names)); function complement(callable $f) { return \(...$args) => !$f(...$args); } $result = Collection::from([1, 2]) ->map(\$v => $v * 2) ->reduce(\($tmp, $v) => $tmp + $v, 0); ``` One argument against this shorter syntax is that it wouldn't be as easy to google as `fn`. However, long term I think everyone would still get used to it, and I'm personally willing to add an answer to the top Stack Overflow search result for "php backslash keyword". The backslash syntax has precedent from Haskell, and also wouldn't introduce any BC break (who knows how many private codebases might already have functions named `fn`).
  104729
March 15, 2019 02:21 larry@garfieldtech.com ("Larry Garfield")
On Thu, Mar 14, 2019, at 3:41 PM, Theodore Brown wrote:

> > As a small update, I've implemented a proof of concept that uses the ($x) > > ==> $x * $multiplier syntax (or $x ==> $x * $multiplier for short) in > > https://github.com/php/php-src/pull/3945. As mentioned in the RFC, this > > requires scanahead in the lexer. > > > > This syntax is in principle still on the table, though personally I prefer > > fn($x, $y) => $x * $y over ($x, $y) ==> $x * $y. The main redeeming quality > > of ==> is that it supports the paren-less variant $x ==> $x. Taking into > > account the lexer hackery it requires (and which will also be required in > > any 3rd party tooling), this would not be my preferred choice. > > > > I agree that the nicest thing about this syntax is the ability to save > an additional 3 characters of boilerplate for the common use case of > single-parameter arrow functions. However, I'm also not a fan of adding > complex code hacks to make the syntax work. > > One alternative that doesn't seem to have had much discussion on list > is the `\($x) => $x * $y` lambda syntax. This would also allow parentheses > to be omitted for single parameters, making it just as terse as the ==> > syntax without the need for any lexer hackery. > > Here's how the examples from the RFC would look: > > ```php > function array_values_from_keys($arr, $keys) { > return array_map(\$x => $arr[$x], $keys); > } > > > $extended = \$c => $callable($factory($c), $c); > > > $this->existingSchemaPaths = array_filter($paths, \$v => in_array($v, $names)); > > > function complement(callable $f) { > return \(...$args) => !$f(...$args); > } > > > $result = Collection::from([1, 2]) > ->map(\$v => $v * 2) > ->reduce(\($tmp, $v) => $tmp + $v, 0); > ``` > > One argument against this shorter syntax is that it wouldn't be as > easy to google as `fn`. However, long term I think everyone would > still get used to it, and I'm personally willing to add an answer > to the top Stack Overflow search result for "php backslash keyword". > > The backslash syntax has precedent from Haskell, and also wouldn't > introduce any BC break (who knows how many private codebases might > already have functions named `fn`).
To clarify a point (not aimed at Theodore in particular, just a general statement), I don't think "saving keystrokes" is really a relevant or compelling argument here for fn() vs. other options. Rather, the intent of making a short-lambda as terse as possible is that it ceases to feel like a function. It's just a logical operation definition that you work into whatever code you're writing. That it is nominally a function ceases to be something you really think about. That's a worthwhile goal that I support. That said, Nikita has laid out a detailed analysis of why various options are or are not feasible in the RFC. I would encourage anyone tempted to bikeshed or suggest alternate syntaxes read that entire section twice before doing so. fn() isn't my preferred choice either, but it keeps the lexer happy, and we really want to keep the lexer happy. So any alternatives need to consider "keep the lexer happy" as a primary design goal. --Larry Garfield
  104733
March 15, 2019 09:45 joshdifabio@gmail.com (Josh Di Fabio)
On Thu, Mar 14, 2019 at 7:42 PM Theodore Brown <theodorejb@outlook.com> wrote:
> > On Thu, March 14, 2019 10:41 AM Nikita Popov ppv@gmail.com> wrote: > > > On Wed, Mar 13, 2019 at 4:56 PM Nikita Popov ppv@gmail.com> wrote: > > > > > Hi internals, > > > > > > Motivated by the recent list comprehensions RFC, I think it's time we took > > > another look at short closures: > > > > > > https://wiki.php.net/rfc/arrow_functions_v2 > > > > As a small update, I've implemented a proof of concept that uses the ($x) > > ==> $x * $multiplier syntax (or $x ==> $x * $multiplier for short) in > > https://github.com/php/php-src/pull/3945. As mentioned in the RFC, this > > requires scanahead in the lexer. > > > > This syntax is in principle still on the table, though personally I prefer > > fn($x, $y) => $x * $y over ($x, $y) ==> $x * $y. The main redeeming quality > > of ==> is that it supports the paren-less variant $x ==> $x. Taking into > > account the lexer hackery it requires (and which will also be required in > > any 3rd party tooling), this would not be my preferred choice. > > > > I agree that the nicest thing about this syntax is the ability to save > an additional 3 characters of boilerplate for the common use case of > single-parameter arrow functions. However, I'm also not a fan of adding > complex code hacks to make the syntax work. > > One alternative that doesn't seem to have had much discussion on list > is the `\($x) => $x * $y` lambda syntax. This would also allow parentheses > to be omitted for single parameters, making it just as terse as the ==> > syntax without the need for any lexer hackery. > > Here's how the examples from the RFC would look: > > ```php > function array_values_from_keys($arr, $keys) { > return array_map(\$x => $arr[$x], $keys); > } > > > $extended = \$c => $callable($factory($c), $c); > > > $this->existingSchemaPaths = array_filter($paths, \$v => in_array($v, $names)); > > > function complement(callable $f) { > return \(...$args) => !$f(...$args); > } > > > $result = Collection::from([1, 2]) > ->map(\$v => $v * 2) > ->reduce(\($tmp, $v) => $tmp + $v, 0); > ``` > > One argument against this shorter syntax is that it wouldn't be as > easy to google as `fn`. However, long term I think everyone would > still get used to it, and I'm personally willing to add an answer > to the top Stack Overflow search result for "php backslash keyword". > > The backslash syntax has precedent from Haskell, and also wouldn't > introduce any BC break (who knows how many private codebases might > already have functions named `fn`). > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php >
I'd certainly be on board with the fn() syntax, but the backslash syntax has definitely grown on me. To me, all of the examples in Theodore's email are very readable and I find that the backslash makes it very easy to identify arrow functions when grokking. array_filter($numbers, \$n => $n % 2); vs. array_filter($numbers, fn($n) => $n % 2); When grokking these two pieces of code, I immediately see "\$n => $n % 2" as a single unit, whereas in the latter example I instinctively (and incorrectly) interpret "fn($n)" as an expression. When parens are required, the difference is obviously reduced, but I think I still prefer the backslash syntax since the LHS doesn't grok as a function call. reduce($numbers, \($x, $y) => $x + $y); vs. reduce($numbers, fn($x, $y) => $x + $y); That said, I'd personally take either syntax gladly.
  104761
March 15, 2019 21:44 theodorejb@outlook.com (Theodore Brown)
On Fri, March 15, 2019 at 4:45 AM Josh Di Fabio <joshdifabio@gmail.com> wrote:

> I'd certainly be on board with the fn() syntax, but the backslash > syntax has definitely grown on me. To me, all of the examples in > Theodore's email are very readable and I find that the backslash makes > it very easy to identify arrow functions when grokking. > > `array_filter($numbers, \$n => $n % 2);` > vs. > `array_filter($numbers, fn($n) => $n % 2);` > > When grokking these two pieces of code, I immediately see `\$n => $n % > 2` > as a single unit, whereas in the latter example I instinctively > (and incorrectly) interpret "fn($n)" as an expression. > > When parens are required, the difference is obviously reduced, but I > think I still prefer the backslash syntax since the LHS doesn't grok > as a function call. > > `reduce($numbers, \($x, $y) => $x + $y);` > vs. > `reduce($numbers, fn($x, $y) => $x + $y);` > > That said, I'd personally take either syntax gladly.
You have a good point about `fn()` looking like an expression. That said, since it would be a keyword IDEs will highlight it differently which should help avoid confusion. Regarding the backslash syntax, I forgot to check how it looks with by-reference passing and returning before I sent my email. Here are those examples: ```php fn(&$x) => $x; fn&($x) => $x; // vs. \(&$x) => $x; \&($x) => $x; // unclear if passing or returning by reference \&$x => $x; // and worst of all... fn&(&$x) => $x; // vs. \&(&$x) => $x; ``` I have to admit that the `fn` prefix is a little more readable for these use cases (though I've never actually seen a real function using by-reference passing and returning at the same time). -Theodore Brown