Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:88628 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 69869 invoked from network); 2 Oct 2015 00:09:42 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 2 Oct 2015 00:09:42 -0000 X-Host-Fingerprint: 68.118.157.39 68-118-157-39.dhcp.mdsn.wi.charter.com Received: from [68.118.157.39] ([68.118.157.39:5779] helo=localhost.localdomain) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 1E/10-03039-44BCD065 for ; Thu, 01 Oct 2015 20:09:41 -0400 Message-ID: <1E.10.03039.44BCD065@pb1.pair.com> To: internals@lists.php.net References: Date: Thu, 1 Oct 2015 19:09:37 -0500 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.2.0 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit X-Posted-By: 68.118.157.39 Subject: Re: [PHP-DEV] Re: Arrow function expressions in PHP From: me@stephencoakley.com (Stephen Coakley) On 10/01/2015 06:52 AM, Levi Morrison wrote: > On Wed, Sep 30, 2015 at 11:59 PM, Stephen Coakley 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", >>> "Beryl­lium" >>> ]; >>> >>> 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