[RFC] Arrow functions / short closures

  104693
March 13, 2019 15:56 nikita.ppv@gmail.com (Nikita Popov)
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

This is based on a previous (withdrawn) proposal by Levi & Bob. It uses the
syntax

    fn($x) => $x * $multiplier

and implicit by-value variable binding. This example is roughly equivalent
to:

    function($x) use($multiplier) { return $x * $multiplier; }

The RFC contains a detailed discussion of syntax choices and binding modes.

Regards,
Nikita
  104694
March 13, 2019 19:36 ben@benramsey.com (Ben Ramsey)
> On Mar 13, 2019, at 10:56, 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 > > This is based on a previous (withdrawn) proposal by Levi & Bob. It uses the > syntax > > fn($x) => $x * $multiplier > > and implicit by-value variable binding. This example is roughly equivalent > to: > > function($x) use($multiplier) { return $x * $multiplier; } > > The RFC contains a detailed discussion of syntax choices and binding modes.
Has the use of `f` as a leading symbol been considered and discussed? For example: f($x) => $x * $multiplier Cheers, Ben
  104696
March 13, 2019 20:26 travis.font@gmail.com (Travis van der Font)
Arrow functions are ternary operators to functions.
While they are nice and shorten, they can be hard to read at times;
considerably to people who aren't used to them which is surprisedly a
majority of PHP programmers.

Having them optional sure, but not necessary.

Feel free to decide between fn() or f() as both are equivalently
comprehensible to the same level of minimalism.
Anyone considered? ($x) => $x * $multiplier

#mytwocents

Kind regards / Léif Gréiss,
Travis van Font


Le mer. 13 mars 2019 à 16:57, Nikita Popov ppv@gmail.com> a écrit :

> 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 > > This is based on a previous (withdrawn) proposal by Levi & Bob. It uses the > syntax > > fn($x) => $x * $multiplier > > and implicit by-value variable binding. This example is roughly equivalent > to: > > function($x) use($multiplier) { return $x * $multiplier; } > > The RFC contains a detailed discussion of syntax choices and binding modes. > > Regards, > Nikita >
  104697
March 13, 2019 20:37 chasepeeler@gmail.com (Chase Peeler)
On Wed, Mar 13, 2019 at 4:26 PM Travis van der Font font@gmail.com>
wrote:

> Arrow functions are ternary operators to functions. > While they are nice and shorten, they can be hard to read at times; > considerably to people who aren't used to them which is surprisedly a > majority of PHP programmers. > > Having them optional sure, but not necessary. > > Feel free to decide between fn() or f() as both are equivalently > comprehensible to the same level of minimalism. > Anyone considered? ($x) => $x * $multiplier > > I use this format a lot in javascript, so I like not having any indicator.. I can't remember which language it was, but they didn't even require the
parentheses if there was only a single input: $x => $x * $multiplier;
> #mytwocents > > Kind regards / Léif Gréiss, > Travis van Font > > > Le mer. 13 mars 2019 à 16:57, Nikita Popov ppv@gmail.com> a écrit > : > > > 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 > > > > This is based on a previous (withdrawn) proposal by Levi & Bob. It uses > the > > syntax > > > > fn($x) => $x * $multiplier > > > > and implicit by-value variable binding. This example is roughly > equivalent > > to: > > > > function($x) use($multiplier) { return $x * $multiplier; } > > > > The RFC contains a detailed discussion of syntax choices and binding > modes. > > > > Regards, > > Nikita > > > --
-- Chase chasepeeler@gmail.com
  104699
March 13, 2019 20:56 rowan.collins@gmail.com (Rowan Collins)
On 13/03/2019 20:37, Chase Peeler wrote:
>> Anyone considered? ($x) => $x * $multiplier >> >> I use this format a lot in javascript, so I like not having any indicator. > I can't remember which language it was, but they didn't even require the > parentheses if there was only a single input: > $x => $x * $multiplier;
I suggest you both read the RFC; about half the page is dedicated to summarising the syntaxes which have been considered, and the pros and cons of each. The summary of that syntax begins: > This is both the most popular and the most technically infeasible syntax. Regards, -- Rowan Collins [IMSoP]
  104700
March 13, 2019 21:06 ben@benramsey.com (Ben Ramsey)
> On Mar 13, 2019, at 15:56, Rowan Collins collins@gmail.com> wrote: > > I suggest you both read the RFC; about half the page is dedicated to summarising the syntaxes which have been considered, and the pros and cons of each. > > The summary of that syntax begins: > > > This is both the most popular and the most technically infeasible syntax.
I did read the RFC, and I could find no mention of f(), so I’ll rephrase my question. :-) I would like to consider `f` as a potential leading symbol instead of `fn`. Cheers, Ben
  104704
March 13, 2019 23:01 bjorn.x.larsson@telia.com (=?UTF-8?Q?Bj=c3=b6rn_Larsson?=)
Den 2019-03-13 kl. 16:56, skrev Nikita Popov:
> 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 > > This is based on a previous (withdrawn) proposal by Levi & Bob. It uses the > syntax > > fn($x) => $x * $multiplier > > and implicit by-value variable binding. This example is roughly equivalent > to: > > function($x) use($multiplier) { return $x * $multiplier; } > > The RFC contains a detailed discussion of syntax choices and binding modes. > > Regards, > Nikita
Thanks for bringing this forward Nikita! I recall from the earlier discussions 2017 that also the lambda as a keyword was considered. I would like to bring that forward as one option, maybe less impact on existing code as a reserved keyword. An advantage of not having a keyword /prefix could be that for simple arrow functions no parenthesis is needed and readability is improved: $someDict->map(fn($v) => $v * 2)->filter(fn($v) => $v % 3); $someDict->map(\($v) => $v * 2)->filter(\($v) => $v % 3); $someDict->map(^($v) => $v * 2)->filter(^($v) => $v % 3); vs $someDict->map($v ==> $v * 2)->filter($v ==> $v % 3); $someDict->map($v ~> $v * 2)->filter($v ~> $v % 3); r//Björn L
  104705
March 14, 2019 02:29 larry@garfieldtech.com ("Larry Garfield")
On Wed, Mar 13, 2019, at 11:57 AM, Nikita Popov 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 > > This is based on a previous (withdrawn) proposal by Levi & Bob. It uses the > syntax > > fn($x) => $x * $multiplier > > and implicit by-value variable binding. This example is roughly equivalent > to: > > function($x) use($multiplier) { return $x * $multiplier; } > > The RFC contains a detailed discussion of syntax choices and binding modes. > > Regards, > Nikita
A very thorough writeup, thanks, Nikita! +1 from me. Two questions: The section on static arrow functions implies, but doesn't say outright, that $this will auto-bind just like any other variable if used inside an arrow function. Is that the case? If so, that should be made explicit for clarity. With regards to comprehensions, this RFC says that arrow functions have a single, implicitly returned statement. How would that play with a function body that includes a yield? Would that work "as expected" (the arrow function becomes a generator function that can be iterated, making it functionally a comprehension) or would it fail in some mysterious way? --Larry Garfield
  104707
March 14, 2019 04:01 david.proweb@gmail.com (David Rodrigues)
I have two doubts about the RFC:

1. Your example with "($x) => $x" consider the use of "$x => $x", but not
specifically "($x) => $x". I mean: maybe it can accept "($x) => $x" but not
"$x => $x" because of the array conflict (as mentioned), and with that we
avoid to create a new keyword "fn". So parentheses is required.

2. I don't remember. haha

Em qua, 13 de mar de 2019 às 12:57, Nikita Popov ppv@gmail.com>
escreveu:

> 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 > > This is based on a previous (withdrawn) proposal by Levi & Bob. It uses the > syntax > > fn($x) => $x * $multiplier > > and implicit by-value variable binding. This example is roughly equivalent > to: > > function($x) use($multiplier) { return $x * $multiplier; } > > The RFC contains a detailed discussion of syntax choices and binding modes. > > Regards, > Nikita >
-- David Rodrigues
  104708
March 14, 2019 06:16 rowan.collins@gmail.com (Rowan Collins)
On 14 March 2019 04:01:54 GMT+00:00, David Rodrigues proweb@gmail.com> wrote:
>1. Your example with "($x) => $x" consider the use of "$x => $x", but >not >specifically "($x) => $x". I mean: maybe it can accept "($x) => $x" but >not >"$x => $x" because of the array conflict (as mentioned), and with that >we >avoid to create a new keyword "fn". So parentheses is required.
I don't think this helps, because you can put brackets around any expression, for precedence, and any expression can appear on the left of an array literal: $foo = [ ($bar + 1) * 2 => $baz ]; So the following, while redundant, is currently valid: $foo = [ ($bar) => $baz ]; Regards, -- Rowan Collins [IMSoP]
  104717
March 14, 2019 15:38 david.proweb@gmail.com (David Rodrigues)
Em qui, 14 de mar de 2019 às 03:17, Rowan Collins collins@gmail..com>
escreveu:

> I don't think this helps, because you can put brackets around any > expression, for precedence, and any expression can appear on the left of an > array literal: > > $foo = [ ($bar + 1) * 2 => $baz ]; > > So the following, while redundant, is currently valid: > > $foo = [ ($bar) => $baz ]; > Yeah, I don't think in that case. And maybe using [] instead of ()? For
instance: "[$x] => $x + 1". PHP don't supports and array as key, so maybe it will not causes any conflict. -- David Rodrigues
  104710
March 14, 2019 09:26 nikita.ppv@gmail.com (Nikita Popov)
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 > > This is based on a previous (withdrawn) proposal by Levi & Bob. It uses > the syntax > > fn($x) => $x * $multiplier > > and implicit by-value variable binding. This example is roughly equivalent > to: > > function($x) use($multiplier) { return $x * $multiplier; } > > The RFC contains a detailed discussion of syntax choices and binding modes. >
Trying to reply to everyone in one mail... 1. Using f() =>. This is possible. However, f would become a reserved keyword, and I'm very leery of making a single-character reserved keyword. Additionally I think that the fn() => syntax is more intuitive, but maybe that's my Rust influences speaking ;) 2. $this binding. Yes, $this is indeed bound exactly as with normal closures. I've explicitly mentioned this in the RFC now. 3. yield. Using yield is per-se not a problem. You can write `() => yield 42`, which is basically `function() { return yield 42; }`, which is okay, because yield is an expression and generators can return values since PHP 7. It also doesn't seem particularly useful though, as you're limited to yielding a single value. However, in the current form short closures are a replacement for list comprehensions only insofar they make the use of map() and filter() non-torturous. You couldn't actually use them to get a comprehension-like syntax as suggested in the other thread: $gen = (fn() => foreach ($list as $elem) yield $elem * 2))(); // would be, hypothetically: $gen = (function() use($list) { return foreach ($list as $elem) yield $elem * 2; }))(); The yield part here isn't the problem, "return foreach" is. Unlike yield, foreach is a statement, not an expression. To make this work we'd need a block form of short closures: $gen = (fn() { foreach ($list as $elem) yield $elem * 2; })(); // or maybe $gen = (fn() => { foreach ($list as $elem) yield $elem * 2; })(); Either way, I wouldn't want to read that code ;) The right way to write this is $list->map(fn($elem) => $elem * 2). 4. ($x) => $x instead of $x => $x. As Rowan mentioned, this doesn't really change things. Where $x is valid, ($x) is in most cases also valid. Regards, Nikita
  104711
March 14, 2019 13:44 rowan.collins@gmail.com (Rowan Collins)
On 13 March 2019 15:56:40 GMT+00:00, Nikita Popov ppv@gmail.com> wrote:
>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
Hi Nikita, Thanks for reviving this. I think the RFC does a great job of justifying both the syntax and the behaviour, and focusing on the most used features without ruling out further additions in future. In particular, I think whether and how we support auto-capture in full function bodies has its own pros and cons to debate, separate from auto-capture as a convenience in short "lambda" expressions. I was initially quite drawn to a block-style syntax. As someone not particularly familiar with functional programming (which I suspect puts me in the majority of PHP users), I actually find it easier to spot this as "a closure returning a closure": { ($x) => { ($y) => $x * $y } } rather than having to work out the associativity of chained operators: fn($x) => fn($y) => $x * $y However, I imagine the only people who will heavily use such a construct will be those who *are* familiar with functional programming in other languages, and will find the simpler syntax more familiar and convenient. It would also be ugly to add full block bodies in a similar style like { ($x) => { ... } }, and while I'm not convinced we need that, we probably don't want to rule it out completely. As such, I support the RFC in its current form. Regards, -- Rowan Collins [IMSoP]
  104713
March 14, 2019 14:12 benjamin.morel@gmail.com (Benjamin Morel)
This makes me thinking, has this syntax been considered?

($x) => { $x * $y }

Nested:

($x) => { ($y) => { $x * $y } }

AFAICS, we don't need the brackets around the whole expression, as this
should be parsable without any ambiguity; and the syntax would be closer to
that of JavaScript.


On Thu, 14 Mar 2019 at 14:44, Rowan Collins collins@gmail.com> wrote:

> On 13 March 2019 15:56:40 GMT+00:00, Nikita Popov ppv@gmail.com> > wrote: > >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 > > Hi Nikita, > > Thanks for reviving this. I think the RFC does a great job of > justifying both the syntax and the behaviour, and focusing on the most > used features without ruling out further additions in future. In > particular, I think whether and how we support auto-capture in full > function bodies has its own pros and cons to debate, separate from > auto-capture as a convenience in short "lambda" expressions. > > I was initially quite drawn to a block-style syntax. As someone not > particularly familiar with functional programming (which I suspect > puts me in the majority of PHP users), I actually find it easier to > spot this as "a closure returning a closure": > > { ($x) => { ($y) => $x * $y } } > > rather than having to work out the associativity of chained operators: > > fn($x) => fn($y) => $x * $y > > However, I imagine the only people who will heavily use such a > construct will be those who *are* familiar with functional programming > in other languages, and will find the simpler syntax more familiar and > convenient. > > It would also be ugly to add full block bodies in a similar style like > { ($x) => { ... } }, and while I'm not convinced we need that, we > probably don't want to rule it out completely. > > As such, I support the RFC in its current form. > > Regards, > -- > Rowan Collins > [IMSoP] > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
  104714
March 14, 2019 14:54 rowan.collins@gmail.com (Rowan Collins)
On Thu, 14 Mar 2019 at 14:12, Benjamin Morel morel@gmail.com>
wrote:

> This makes me thinking, has this syntax been considered? > > ($x) => { $x * $y } > > Nested: > > ($x) => { ($y) => { $x * $y } } >
Wouldn't this have all the same parser problems as the RFC discusses? The problem, as I understand it, is not avoiding ambiguity, it's avoiding lookahead. If you write: $foo = [ ($x) => { $x } ]; $bar = [ ($x) => $x ]; The parser has already consumed " [ ($x) =>" before it can decide if each ($x) is an array key or a closure signature. It's parseable, but only using one of the workarounds described in the RFC. If I'm understanding the RFC correctly, the only way to avoid that is to have closures *start* differently from other valid constructs, because then the parser doesn't need to recurse / backtrack / etc. Regards, -- Rowan Collins [IMSoP]
  104715
March 14, 2019 15:09 benjamin.morel@gmail.com (Benjamin Morel)
> > The problem, as I understand it, is not avoiding ambiguity, it's avoiding > lookahead.
You're right, I was only thinking about resolving the ambiguity with array keys. It's too bad if the parser implementation considerations take precedence over the purity of the language, but I can understand the maintenance nightmare that people are trying to avoid here. Something else that crosses my mind, is: what prevents us from using the same syntax as ES6: ($x) => $x * $y But to prevent any ambiguity, forbid array keys from being enclosed with parenthese? For example, the following would be considered an array containing a closure: [ ($foo) => "bar" ] And the following would just become a syntax error: [ ("foo") => "bar" ] Would that solve the parser problem? BC-wise, I don't think this would be much of a problem: I have yet to see array keys enclosed with parentheses in PHP codebases. On Thu, 14 Mar 2019 at 15:54, Rowan Collins collins@gmail.com> wrote:
> On Thu, 14 Mar 2019 at 14:12, Benjamin Morel morel@gmail.com> > wrote: > > > This makes me thinking, has this syntax been considered? > > > > ($x) => { $x * $y } > > > > Nested: > > > > ($x) => { ($y) => { $x * $y } } > > > > > Wouldn't this have all the same parser problems as the RFC discusses? > > The problem, as I understand it, is not avoiding ambiguity, it's avoiding > lookahead. If you write: > > $foo = [ ($x) => { $x } ]; > $bar = [ ($x) => $x ]; > > The parser has already consumed " [ ($x) =>" before it can decide if each > ($x) is an array key or a closure signature. It's parseable, but only using > one of the workarounds described in the RFC. > > If I'm understanding the RFC correctly, the only way to avoid that is to > have closures *start* differently from other valid constructs, because then > the parser doesn't need to recurse / backtrack / etc. > > Regards, > -- > Rowan Collins > [IMSoP] >
  104719
March 14, 2019 15:49 rowan.collins@gmail.com (Rowan Collins)
On Thu, 14 Mar 2019 at 15:10, Benjamin Morel morel@gmail.com>
wrote:

> The problem, as I understand it, is not avoiding ambiguity, it's avoiding >> lookahead. > > > You're right, I was only thinking about resolving the ambiguity with array > keys. It's too bad if the parser implementation considerations take > precedence over the purity of the language >
I don't agree that this has anything to do with "purity". If JS didn't have => syntax, why would "fn is a shorter keyword than function" not be a good enough reason to go with this syntax? I think it's equally valid to say that it would be a shame for one syntax shortcut to take precedence over the consistency and maintainability of everything else in the language. Is it really that important to save two key strokes per closure?
> Would that solve the parser problem? BC-wise, I don't think this would be > much of a problem: I have yet to see array keys enclosed with parentheses > in PHP codebases. >
A quick regex search in a code base I had to hand found a few instances of casts, like [(string)$foo => $bar], but indeed no instances of bracketed variables. However, it would feel rather bizarre to be able to write this: [ $x+1 => $foo ] But not this: [ ($x+1) => $foo ] Which I think would have to be the implication for this to make the parsing any easier. On Thu, 14 Mar 2019 at 15:38, David Rodrigues proweb@gmail.com> wrote:
> maybe using [] instead of ()? For instance: "[$x] => $x + 1". PHP don't supports and array as key, so maybe it will not causes any conflict.
Again, I think this solves the ambiguity but not the parsing, although I could be wrong. Consider parsing these three expressions: [ [ $foo ] ] # Nested arrays [ [ $foo => $bar ] ] # Nested arrays with an explicit key [ [ $foo ] => $bar ] # An array containing a closure Regards, -- Rowan Collins [IMSoP]
  104720
March 14, 2019 17:19 joshdifabio@gmail.com (Josh Di Fabio)
On Thu, Mar 14, 2019 at 3:49 PM Rowan Collins collins@gmail.com> wrote:
> > Is it really that important to save two key strokes per closure? >
I'd say that the (probably overwhelming) majority of arrow functions have a single parameter and, in those cases, the JS syntax saves four characters, ignoring whitespace. Another way of looking at it is that you have six characters of boilerplate with the fn() syntax versus two in JS (ignoring whitespace) for single param arrow functions.
  104721
March 14, 2019 17:25 george.banyard@gmail.com ("G. P. B.")
On Thu, 14 Mar 2019 at 18:20, Josh Di Fabio <joshdifabio@gmail.com> wrote:

> On Thu, Mar 14, 2019 at 3:49 PM Rowan Collins collins@gmail.com> > wrote: > > > > Is it really that important to save two key strokes per closure? > > > > I'd say that the (probably overwhelming) majority of arrow functions > have a single parameter and, in those cases, the JS syntax saves four > characters, ignoring whitespace. Another way of looking at it is that > you have six characters of boilerplate with the fn() syntax versus two > in JS (ignoring whitespace) for single param arrow functions. >
No offense but at the end of the day having readable code is better than shorter code IMHO, so I don't really see the problem with having a couple of extra character/keystrokes as this is more readable then the massive boilerplate we currently have. However, I can see people beeing fond of the concise syntax of JS Best regards George P. Banyard
  104712
March 14, 2019 14:03 mathieu@rochette.cc (Mathieu Rochette)
Hi,

it's nice to see this going on again :)

while reading the rfc I was wondering, why do we need the "static" keyword, couldn't static function be detected automatically ? 

I guess this apply to the existing closure syntax as well so to get more on this topic I'll share my preferences on the syntax: I like the ==> or ~> version because it also allow to drop the parenthesis when there is only one argument and it's closer to what I'm used to in javascript

I wouldn't mind having the rust syntax too but yeah, it would feel a bit odd in PHP


thank you for your work on this !

Nikita Popov – Wed, 13. March 2019 16:57
> Hi internals, > > Motivated by the recent list comprehensions RFC, I think it's time we took > another look at short closures: > > wiki.php.net/rfc/arrow_functions_v2 > > This is based on a previous (withdrawn) proposal by Levi & Bob. It uses the > syntax > > fn($x) => $x * $multiplier > > and implicit by-value variable binding. This example is roughly equivalent > to: > > function($x) use($multiplier) { return $x * $multiplier; } > > The RFC contains a detailed discussion of syntax choices and binding modes. > > Regards, > Nikita
  104737
March 15, 2019 10:02 nikita.ppv@gmail.com (Nikita Popov)
On Thu, Mar 14, 2019 at 3:04 PM Mathieu Rochette <mathieu@rochette.cc>
wrote:

> > Hi, > > it's nice to see this going on again :) > > while reading the rfc I was wondering, why do we need the "static" > keyword, couldn't static function be detected automatically ? >
I've added a note regarding this in https://wiki.php.net/rfc/arrow_functions_v2#this_binding_and_static_arrow_functions. The problem is that in PHP we cannot actually reliably detect whether or not a closure uses $this. Sure -- we can see literal $this uses, but some of them are implicit: fn() => Foo::bar() can make use of $this if $this is scope-compatible with Foo. fn() => call_user_func('Foo::bar') can as well. fn() => $a($b) can as well, if it so happens that $a = 'call_user_func' and $b = 'Foo::bar'. The result is that we cannot reliably detect whether $this is used or not -- we can only make a conservative analysis that may sometimes bind $this even though it's not needed. In which case I would prefer going with the more predictable behavior of always binding $this, and using "static" in the rare cases where someone really cares about this.
> I guess this apply to the existing closure syntax as well so to get more > on this topic I'll share my preferences on the syntax: I like the ==> or ~> > version because it also allow to drop the parenthesis when there is only > one argument and it's closer to what I'm used to in javascript > > I wouldn't mind having the rust syntax too but yeah, it would feel a bit > odd in PHP > > > thank you for your work on this ! > > Nikita Popov – Wed, 13. March 2019 16:57 > > Hi internals, > > > > Motivated by the recent list comprehensions RFC, I think it's time we > took > > another look at short closures: > > > > wiki.php.net/rfc/arrow_functions_v2 > > > > This is based on a previous (withdrawn) proposal by Levi & Bob. It uses > the > > syntax > > > > fn($x) => $x * $multiplier > > > > and implicit by-value variable binding. This example is roughly > equivalent > > to: > > > > function($x) use($multiplier) { return $x * $multiplier; } > > > > The RFC contains a detailed discussion of syntax choices and binding > modes. > > > > Regards, > > Nikita >
  104718
March 14, 2019 15:41 nikita.ppv@gmail.com (Nikita Popov)
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 > > This is based on a previous (withdrawn) proposal by Levi & Bob. It uses > the syntax > > fn($x) => $x * $multiplier > > and implicit by-value variable binding. This example is roughly equivalent > to: > > function($x) use($multiplier) { return $x * $multiplier; } > > The RFC contains a detailed discussion of syntax choices and binding modes. > > Regards, > Nikita >
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. Regards, Nikita
  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
  104731
March 15, 2019 09:01 drealecs@gmail.com (=?UTF-8?Q?Alexandru_P=C4=83tr=C4=83nescu?=)
Hi,

To start with, I personally understand why a prefix character is needed
before parenthesis to make the parser simpler. I would like another simpler
option but will have to investigate more on this.

My question would be: whatever syntax we are going to use that has arrow
syntax, let's say *$f = \($x) => $x * 2;* are we going to also support the
arrow block version?:
*$f = \($x) => {*
*    // more operations that will have better visible on multi-line*

*    return $x * 2;*
*}*

This is present in other languages and was thinking that we could have it
in PHP also.

Regards,
Alex


On Wed, Mar 13, 2019 at 5:57 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 > > This is based on a previous (withdrawn) proposal by Levi & Bob. It uses the > syntax > > fn($x) => $x * $multiplier > > and implicit by-value variable binding. This example is roughly equivalent > to: > > function($x) use($multiplier) { return $x * $multiplier; } > > The RFC contains a detailed discussion of syntax choices and binding modes. > > Regards, > Nikita >
  104732
March 15, 2019 09:30 rowan.collins@gmail.com (Rowan Collins)
On Fri, 15 Mar 2019 at 09:01, Alexandru Pătrănescu <drealecs@gmail.com>
wrote:

> My question would be: whatever syntax we are going to use that has arrow > syntax, let's say *$f = \($x) => $x * 2;* are we going to also support the > arrow block version?: > *$f = \($x) => {* > * // more operations that will have better visible on multi-line* > > * return $x * 2;* > *}* >
See "Future Scope" in the RFC:
> This feature is omitted in this RFC, because the value-proposition of this syntax is much smaller: Once you have multiple statements, the
relative overhead of the conventional closure syntax becomes small. We shouldn't pick a syntax that rules it out, but it can be added later, with a separate RFC to discuss the benefits and details. Regards, -- Rowan Collins [IMSoP]
  104738
March 15, 2019 10:10 nikita.ppv@gmail.com (Nikita Popov)
On Fri, Mar 15, 2019 at 10:31 AM Rowan Collins collins@gmail.com>
wrote:

> On Fri, 15 Mar 2019 at 09:01, Alexandru Pătrănescu <drealecs@gmail.com> > wrote: > > > My question would be: whatever syntax we are going to use that has arrow > > syntax, let's say *$f = \($x) => $x * 2;* are we going to also support > the > > arrow block version?: > > *$f = \($x) => {* > > * // more operations that will have better visible on multi-line* > > > > * return $x * 2;* > > *}* > > > > > See "Future Scope" in the RFC: > > > This feature is omitted in this RFC, because the value-proposition of > this syntax is much smaller: Once you have multiple statements, the > relative overhead of the conventional closure syntax becomes small. > > We shouldn't pick a syntax that rules it out, but it can be added later, > with a separate RFC to discuss the benefits and details. >
It might be worth giving some consideration to the possibility of introducing this syntax already in this RFC. The main problem with punting this off for later is that it may be necessary to refactor closures between the short and the long form regularly: You need an extra statement in the closure? You have to switch closure types, and also not forget to write our the use() list this time. On the other hand, allowing a block body for the closure does add a number of complications to this proposal: 1. Syntax choice. Given the fn() syntax, we could go for either fn() {} or fn() => {}. 2. By-ref binding: While by-reference binding is not useful for single-expression closures, there will be cases where it's needed for block closures. We will also need to choose a syntax to opt-in to by-reference binding. The RFC suggests use(&), which is somewhat non-great. 3. Determining bound variables. For single-expression closures we can get away with binding all variables that are used inside the closure. Writing something like fn() $a = $b might cause an unnecessary binding of $a, but it's also a very contrived situation. For block closures performing assignments inside the closure will be much more common and will need some more consideration to avoid unnecessary bindings. Regards, Nikita
  104740
March 15, 2019 10:50 rowan.collins@gmail.com (Rowan Collins)
On Fri, 15 Mar 2019 at 10:10, Nikita Popov ppv@gmail.com> wrote:

> It might be worth giving some consideration to the possibility of > introducing this syntax already in this RFC. The main problem with punting > this off for later is that it may be necessary to refactor closures between > the short and the long form regularly: You need an extra statement in the > closure? You have to switch closure types, and also not forget to write our > the use() list this time. >
I know I'm not necessarily in the majority, here, but to me, that's a feature not a bug: short closures should not be a replacement for every anonymous function, they should be for those cases where you have a really simple expression. At some point, you should be saying "this is now too long for a short closure, it needs a clearer definition", and "more than one expression or statement" is as good a heuristic for that as you're likely to get. I also have strong reservations about generalising automatic binding to full function bodies, because it fundamentally changes the way variable scope works in the language: right now, $foo is always local to a function, unless *explicitly* listed as a parameter, imported with "global", or captured with "use". That's a lot simpler than the scoping rules of a lot of other languages, and it's a guarantee we shouldn't break lightly. I'm willing to be convinced - or simply "out-voted" - on these points, but I think they deserve their own discussion. I was shot down on chat the other day for a naive remark that capturing a large number of variables in a closure would be "doing it wrong"; I'd love to see examples of where this is useful / necessary, and a new RFC would be the perfect place for someone to put those. That could mean another RFC proposed immediately after this one passes, and the feature arriving in the same PHP version, but I don't think we need to merge the two discussions - as long as we keep the possibility in mind when choosing a base syntax. Regards, -- Rowan Collins [IMSoP]