On 10/01/2015 06:52 AM, Levi Morrison wrote:
> On Wed, Sep 30, 2015 at 11:59 PM, Stephen Coakley
<m...@stephencoakley.com> wrote:
>> 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.
>
> I am definitely not proposing to remove `function` at this time. That
> would be a huge BC break, indeed! I meant only that `fn` can be used
> for brevity if that is preferred and it *could* be used in other
> places as well. This is my preference, but I know other people don't
> like that, which is why I would intend to keep it as a separate vote
> from the arrow part (=> expr).
>
That sounds like a good plan. At this time then, a
$square = function(int $x) => $x * $x;
syntax with auto variable capture is what I'm currently hoping for. :)
--
Stephen
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php