rfc:arrow_functions

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
Last revision Both sides next revision
rfc:arrow_functions [2016/09/27 18:12]
levim Pointless edit to get the revision number of previous "current"
rfc:arrow_functions [2017/09/22 13:28]
127.0.0.1 external edit
Line 1: Line 1:
 ====== PHP RFC: Arrow Functions ====== ====== PHP RFC: Arrow Functions ======
-  * Version: 1.2+  * Version: 1.3
   * Date: 2016-08-14   * Date: 2016-08-14
   * Author: Levi Morrison <levim@php.net>   * Author: Levi Morrison <levim@php.net>
   * Author: Bob Weinand <bwoebi@php.net>   * Author: Bob Weinand <bwoebi@php.net>
-  * Status: Draft+  * Status: Under Discussion
   * First Published at: http://wiki.php.net/rfc/arrow_functions   * First Published at: http://wiki.php.net/rfc/arrow_functions
  
Line 10: Line 10:
  
 ===== Introduction ===== ===== Introduction =====
-Anonymous functions and closures can be verbose even though sometimes they are quite simple and contain only a single expression. Additionally, importing variables into the closure's scope is manual and is painful overhead for single-expression closures. This RFC proposes a shorter syntax for anonymous functions and closures and makes it easier for these new closures to capture values/variables.+Anonymous functions and closures can be verbose even though sometimes they are quite simple and contain only a single expression. Additionally, importing variables into the closure's scope is manual and is painful overhead for single-expression closures. In practice these single-expression closures are common. This RFC proposes a more concise syntax for this pattern.
  
 As an example of the declaration overhead, consider this function that [[https://github.com/darkskillfr/near2u/blob/5a606fc9082c33c7270d37e4c7d29160586285f8/serveur/lib.php|I found online]]: As an example of the declaration overhead, consider this function that [[https://github.com/darkskillfr/near2u/blob/5a606fc9082c33c7270d37e4c7d29160586285f8/serveur/lib.php|I found online]]:
  
 <PHP>function array_values_from_keys($arr, $keys) { <PHP>function array_values_from_keys($arr, $keys) {
-    return array_map(function($x) use ($arr) { return $arr[$x]; }, $keys);+    return array_map(function ($x) use ($arr) { return $arr[$x]; }, $keys);
 }</PHP> }</PHP>
  
-The closure performs a single operation <php>$arr[$x]</php> and is 8 characters but requires 30 other characters (excluding whitespace). This means that roughly 79% of the closure's code is overhead (30/38). For this RFC these extra characters are called 'boilerplate'. This RFC proposes arrow functions to reduce the amount of boilerplate by having a shorter syntax and importing used variables from the outer scope implicitly. Using all possible features proposed by this RFC this would reduce to the following:+The closure performs a single operation <php>$arr[$x]</php> and is 8 characters but requires 30 other characters (excluding whitespace). This means that roughly 79% of the closure's code is overhead (30/38). For this RFC these extra characters are called 'boilerplate'. This RFC proposes arrow functions to reduce the amount of boilerplate by having a shorter syntax and importing used variables from the outer scope implicitly. Using arrow functions from this RFC this would reduce to the following:
  
 <PHP>function array_values_from_keys($arr, $keys) { <PHP>function array_values_from_keys($arr, $keys) {
-    return array_map($x => $arr[$x], $keys);+    return array_map(fn($x=> $arr[$x], $keys);
 }</PHP> }</PHP>
  
-This reduces the amount of boilerplate from 30 characters down to 5.+This reduces the amount of boilerplate from 30 characters down to 8.
  
 See more examples in the [[#examples|Examples]] section. The [[#longer_examples|longer examples]] may be helpful to those struggling to understand why the RFC authors care about saving symbols and clarity on each closure. See more examples in the [[#examples|Examples]] section. The [[#longer_examples|longer examples]] may be helpful to those struggling to understand why the RFC authors care about saving symbols and clarity on each closure.
Line 31: Line 31:
  
 ===== Proposal ===== ===== Proposal =====
-Arrow functions have a few forms:+Arrow functions have the following form:
  
 <PHP> <PHP>
-(parameter_list) => expr +fn(parameter_list) => expr
-singleParam => expr +
-() => expr+
 </PHP> </PHP>
  
-The ''expr'' is a single expression in all cases. This expression will be evaluated and then the result will be returned:+The ''expr'' is a single expression. This expression will be evaluated and then the result will be returned:
  
 <PHP> <PHP>
-$mul2 = ($x) => $x * 2;+$mul2 = fn($x) => $x * 2;
  
 $mul2(3); // evaluates to 6 $mul2(3); // evaluates to 6
 </PHP> </PHP>
- 
-If there is only a single parameter for the arrow function then the parenthesis around the parameter list can be omitted: 
- 
-<PHP> 
-$mul2 = $x => $x * 2; 
- 
-$mul2(3); // evaluates to 6 
-</PHP> 
- 
-If there are no parameters then the parentheses are required as probably expected: 
- 
-<PHP>$lazy_factory = () => gen_object();</PHP> 
  
 When a variable in the expression is defined in the parent scope it will be captured implicitly by-value. When a variable in the expression is defined in the parent scope it will be captured implicitly by-value.
Line 65: Line 51:
 $y = 1; $y = 1;
  
-$versionA = $x => $x + $y;+$versionA = fn($x=> $x + $y;
  
-$versionB = function($x) use($y) {+$versionB = function ($x) use ($y) {
     return $x + $y;     return $x + $y;
 };</PHP> };</PHP>
Line 76: Line 62:
  
 ==== Type Declarations ==== ==== Type Declarations ====
-This RFC does not permit type declarations for parameters and return types. This issue was noted multiple times on the mailing list during the short closures RFC as something that bothered voters. However, the main purpose of arrow functions is to focus on the functionality by removing boilerplate. Arrow functions have very small bodies because they are single expressions. This makes them easy to audit and type information is not expected to be helpful.+This RFC does support type declarations for parameters and return types. This issue was noted multiple times on the mailing list during the short closures RFC as something that bothered voters. Therefore this RFC permits them but the authors discourage their general use in arrow functions.
  
-==== Ambiguities ==== +Here are some examples to show the syntax:
-Arrow functions have ambiguities with array key definitions and yield expressions that provide a key.+
  
 <PHP> <PHP>
-// Does this mean: +fn (array $x=> $x 
-//   1. Create an array key of`$x` and a value with `$x * 2` +fn ()int => 42
-//   2. Create an array with one value that is an anonymous function +
-[$x => $x * 2] +
- +
-// Does this mean: +
-//   1. Yield a key of `$x` and a value with `$x * 2` +
-//   2. Yield an anonymous function +
-yield $x => $x * 2;+
 </PHP> </PHP>
  
-These ambiguities are solved by preferring the existing meaningsTo obtain the semantics of arrow functions wrap the arrow function in parenthesisFor example:+==== References ==== 
 +Parameters and return values can be passed/returned by referenceAs mentioned elsewhere, implicitly bound variables will be bound //by value// and not //by reference//References go in the usual places:
  
 <PHP> <PHP>
-// Create an array key of`$x` and a value with `$x * 2` +fn &(array &$xs) => $xs 
-[$x =$x * 2];+</PHP>
  
-// Create an array with one member that is an arrow function +==== Static Arrow Functions ==== 
-[($x => $x * 2)];+The implementation currently supports static closures, for example <php>static fn($x=> static::get($x)</php>. While supported it is uncertain whether it should be included in the final version. Having the implementation support it allows testers to determine usefulness and value.
  
-// Yield a key of `$x` and a value with `$x * 2` +==== Ambiguities ==== 
-yield $x => $x * 2;+Arrow functions have no ambiguities, including array key definitions and yield expressions that provide a key. The ''fn'' prefix removes the ambiguities. 
 +==== Backward Incompatible Changes ==== 
 +Unfortunately the ''fn'' keyword must be a full keyword and not just reserved function name; this is to break the ambiguities with ''<nowiki>=></nowiki>'' for array and yield keys.
  
-// Yield an anonymous function +Ilija Tovilo analyzed the top 1,000 PHP repositories on GitHub to find usages of ''fn''. [[https://gist.github.com/morrisonlevi/473a7e0cb6e59c830224b1c71b8da28c|The gist]] provides more information, but the rough findings are that all known existing usages of ''fn'' are in tests except one case where it is a namespace segment.
-yield ($x => $x * 2); +
-</PHP> +
- +
-==== Backward Incompatible Changes ==== +
-There are no backwards incompatible changes.+
  
 ==== Patches and Tests ==== ==== Patches and Tests ====
-An old implementation with tests can be found here: https://github.com/morrisonlevi/php-src/tree/arrow_functions. This patch was feature-complete for an old version of this RFC that used ''^'' to prefix the function expressions. The implementation can still be used but portion that deals with the grammar must be rewritten. +An implementation with tests can be found here: https://github.com/morrisonlevi/php-src/tree/arrow_functions. There are no known issues with it at this time; please build and test it.
- +
-==== PHP Version ==== +
-This RFC targets PHP 7.NEXT, currently version 7.2.+
  
 ==== Voting ==== ==== Voting ====
Line 130: Line 103:
 ----- -----
  
-==== Examples ==== +===== Examples =====
- +
-=== Snippets ===+
 Taken from [[https://github.com/silexphp/Pimple/blob/62b5d317a83b02eea42b5b785b62a29fba458bcf/src/Pimple/Container.php#L242-L244|silexphp/Pimple]]: Taken from [[https://github.com/silexphp/Pimple/blob/62b5d317a83b02eea42b5b785b62a29fba458bcf/src/Pimple/Container.php#L242-L244|silexphp/Pimple]]:
  
Line 140: Line 111:
  
 // with arrow function: // with arrow function:
-$extended = $c => $callable($factory($c), $c);</PHP>+$extended = fn($c=> $callable($factory($c), $c);</PHP>
  
-This reduces the amount of boilerplate from 44 characters down to 4.+This reduces the amount of boilerplate from 44 characters down to 8.
  
 ----- -----
Line 153: Line 124:
  
 // with arrow function // with arrow function
-$this->existingSchemaPaths = array_filter($paths, $v => in_array($v, $names));</PHP>+$this->existingSchemaPaths = array_filter($paths, fn($v=> in_array($v, $names));</PHP>
  
-This reduces the amount of boilerplate from 31 characters down to 4.+This reduces the amount of boilerplate from 31 characters down to 8.
  
 ----- -----
Line 162: Line 133:
  
 <PHP>function complement(callable $f) { <PHP>function complement(callable $f) {
-    return function(... $args) use ($f) {+    return function (... $args) use ($f) {
         return !$f(... $args);         return !$f(... $args);
     };     };
Line 169: Line 140:
 // with arrow function: // with arrow function:
 function complement(callable $f) { function complement(callable $f) {
-    return (... $args) => !$f(... $args);+    return fn(... $args) => !$f(... $args);
 }</PHP> }</PHP>
- 
-=== Longer Examples === 
- 
-Taken from Pinq's example in their [[https://github.com/TimeToogo/Pinq|README.md]] with only some slight modifications: 
- 
-<PHP>$youngPeopleDetails = $people 
-    ->where(function ($row) use($maxAge) { return $row['age'] <= $maxAge; }) 
-    ->orderByAscending(function ($row) { return $row['firstName']; }) 
-    ->thenByAscending(function ($row) { return $row['lastName']; }) 
-    ->take(50) 
-    ->indexBy(function ($row) { return $row['phoneNumber']; }) 
-    ->select(function ($row) {  
-        return [ 
-            'fullName'    => $row['firstName'] . ' ' . $row['lastName'], 
-            'address'     => $row['address'], 
-            'dateOfBirth' => $row['dateOfBirth'], 
-        ];  
-    }); 
-</PHP> 
- 
-With arrow functions: 
- 
-<PHP>$youngPeopleDetails = $people 
-    ->where($row => $row['age'] <= $maxAge) 
-    ->orderByAscending($row => $row['firstName']) 
-    ->thenByAscending(row => $row['lastName']) 
-    ->take(50) 
-    ->indexBy($row => $row['phoneNumber']) 
-    ->select($row => [ 
-        'fullName'    => $row['firstName'] . ' ' . $row['lastName'], 
-        'address'     => $row['address'], 
-        'dateOfBirth' => $row['dateOfBirth'], 
-    ]); 
-</PHP> 
  
 ----- -----
  
-The following examples were given to me by [[https://gist.github.com/tpunt/b4f9bf30f43b9e148b73ce18245ab472|tpunt]]:+The following example was given to me by [[https://gist.github.com/tpunt/b4f9bf30f43b9e148b73ce18245ab472|tpunt]]:
  
 <PHP>$result = Collection::from([1, 2]) <PHP>$result = Collection::from([1, 2])
-    ->map(function($v) {+    ->map(function ($v) {
         return $v * 2;         return $v * 2;
     })     })
-    ->reduce(function($tmp, $v) {+    ->reduce(function ($tmp, $v) {
         return $tmp + $v;         return $tmp + $v;
     }, 0);     }, 0);
Line 222: Line 159:
 // with arrow functions: // with arrow functions:
 $result = Collection::from([1, 2]) $result = Collection::from([1, 2])
-    ->map($v => $v * 2) +    ->map(fn($v=> $v * 2) 
-    ->reduce(($tmp, $v) => $tmp + $v, 0);+    ->reduce(fn($tmp, $v) => $tmp + $v, 0);
  
 echo $result; //6 echo $result; //6
 </PHP> </PHP>
  
------ +===== Future ScopeMulti-Statement Bodies =====
- +
-Here is with our current closures: +
- +
-<PHP>function groupByKey($collection, $key) +
-+
-    $generatorFactory = function () use ($collection, $key) { +
-        return groupBy( +
-            filter( +
-                $collection, +
-                function ($item) use ($key) { +
-                    return isCollection($item) && has($item, $key); +
-                } +
-            ), +
-            function($value) use ($key) { +
-                return get($value, $key); +
-            } +
-        ); +
-    }; +
- +
-    return new Collection($generatorFactory); +
-}</PHP> +
- +
-And with arrow functions: +
- +
-<PHP>function groupByKey($collection, $key) +
-+
-    $generatorFactory = +
-        () => groupBy( +
-            filter( +
-                $collection, +
-                $item => isCollection($item) && has($item, $key) +
-            ), +
-            $value => get($value, $key); +
-        ); +
- +
-    return new Collection($generatorFactory); +
-}</PHP> +
- +
-===== Future Scope ===== +
- +
-==== Multi-Statement Bodies ====+
 Some languages permit multi-statement closures with a syntax like: Some languages permit multi-statement closures with a syntax like:
  
rfc/arrow_functions.txt · Last modified: 2018/06/28 14:35 by levim