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 expression `(x) => x * 2` means to create
an anonymous function with one parameter `x` that will return `x * 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-ES6
var a3 = a.map((s) => s.length); // ES6
There 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 of `($x)` and a value
with `$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 of `($x)` and a value with `$x * 2`
// 2. Yield an anonymous function
yield ($x) => $x * 2;
This is why Bob Weinand [proposed][2] using `~>` instead of `=>`.
However, if we allow type declarations there is another issue. In the
definition `(Type &$x) => expr` the `(Type &$var)` part can parse as
"take constant `Type` and variable `$var` and 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 with `key => value` and 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 the `function` prefix:
// 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 the `function` prefix 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) => expr
By 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 the `function` keyword to `fn`:
[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 both `fn` and `function`.
What concerns do you have about `fn($x) => $x * 2` or `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
If 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.
> I would prefer that we shorten the `function` keyword to `fn`:
Do 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:
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
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php