Re: Arrow function expressions in PHP
| From: | Stephen Coakley | Date: | Thu, 01 Oct 2015 05:59:37 +0000 |
| Subject: | Re: Arrow function expressions in PHP | ||
| References: | 1 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-88590@lists.php.net to get a copy of this message | ||
On 09/26/2015 11:17 AM, Levi Morrison wrote:
(Email in gist format: https://gist.github.com/morrisonlevi/fa7984c04ff176b5a87c) In EcmaScript 2015 (ES6) the expressionIf my opinion is worth anything, I actually like how fn($x) => $x * 2 looks the most. It's fairly short like the original proposal, but has the advantage of *clearly* appearing to be a function. That was a large complaint on the whole "short closures" idea in the first place, and PHP usually does a good job at making code very obvious and clear. So yeah, an "fn" prefix (and requiring parenthesis always) looks very consistent, but still is short.(x) => x * 2means to create an anonymous function with one parameterxthat will returnx * 2. For example:(x) => x * 2 // is equivalent to: function(x) { return x * 2; }A modified example from [documentation by Mozilla Developer Network][1] page demonstrates how they are useful:var a = [ "Hydrogen", "Helium", "Lithium", "Beryllium" ];var a2 = a.map(function(s){ return s.length }); // pre-ES6var a3 = a.map((s) => s.length); // ES6There has been some talk about how we can use arrow function expressions in PHP. In PHP using the same syntax would have some ambiguities:// Does this mean: // 1. Create an array key with the result ofwith($x)and a value$x * 2// 2. Create an array with one value that is an anonymous function [($x) => $x * 2]// Does this mean: // 1. Yield a key with the result ofThis is why Bob Weinand [proposed][2] using($x)and a value with$x * 2// 2. Yield an anonymous function yield ($x) => $x * 2;~>instead of=>. However, if we allow type declarations there is another issue. In the definition(Type &$x) => exprthe(Type &$var)part can parse as "take constantTypeand variable$varand do a bitwise and&operation." After that the=>will be an unexpected token. Even though the rule would be invalid the parser doesn't know that far ahead it will error and it doesn't know which rule to pick. Changing the token from=>to~>doesn't affect this issue. We could solve the first ambiguities with prefering the current meaning withkey => valueand requiring the meaning with closures to wrap them in(). We could solve the latter ambiguity with a backtracking parser since it will eventually error and then know to pick the other rule. However, I really think this is a bad idea. So how can we have shorter closures without this mess? One simple way is to require thefunctionprefix:// clearly an array with an anonymous function [function($x) => $x * 2];// clearly yields an anonymous function yield function($x) => $x * 2;// clearly an anonymous function function(Type &$x) => expr;Requiring thefunctionprefix mitigates one of the value parts of arrow functions: they are short. Another option would be to resolve the ambiguities with keys and values but to change the type information in parameters:(&$input: array) => exprBy putting the type after the variable (similar to how we declare return types) we no longer have the issues with mis-parsing. Of course, that's not how we declare parameter types currently. I think we would need to permit it everywhere and deprecate the current syntax with the type being prefixed. (By deprecate I mean in PHP 8 and not remove it until PHP 9 or later) I would prefer that we shorten thefunctionkeyword tofn:[fn($x) => $x * 2]This preserves the shortness of the expression while providing unambiguous, simple parsing. Of course, now we have a similar issue: we have bothfnandfunction. What concerns do you have aboutfn($x) => $x * 2or `function($x) => $x * 2`? I will be writing a proper RFC later but I wanted to get discussion going now. [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions [2]: https://wiki.php.net/rfc/short_closures
I would prefer that we shorten theDo you mean generally, or just in short closures? Turning the keyword everywhere would be a huge BC break (though pretty easy to fix in code: "s/function\s/fn /g" :-) ). I'd be OK with allowing both everywhere for consistency though:functionkeyword tofn:
fn square(int $x) {
return $x * $x;
}
$squaresPlusOne = array_map(function(int $x) => square($x) + 1, [1, 2, 3, 4]);
class Foo {
public fn __construct() {}
}
You get the idea...
I actually really like that + your idea. Kudos.
--
Stephen