rfc:arrow_function_preference

Differences

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

Link to this comparison view

rfc:arrow_function_preference [2017/07/05 06:02]
levim Add 3rd option?
rfc:arrow_function_preference [2017/09/22 13:28]
Line 1: Line 1:
-===== Introduction ===== 
- 
-It has been expressed both on and off the mailing list that [[rfc:​arrow_functions|arrow functions]] are a highly desired feature. It seems that we mostly do not agree on //how// this feature should work. The purpose of this document and its corresponding vote is to help us come to consensus. 
- 
-The result of this vote is non-binding,​ meaning that voting here is not accepting something into the language. This is a way to express your preference of the two proposed syntaxes in voting form. The result of this vote will be used to draft a final RFC targeting PHP 7.2. 
- 
-===== Concerns ===== 
- 
-Having reviewed the past discussions about arrow functions I have determined that there are a few main points that people disagree on. The ordering here has no significance;​ they are numbered only so it is easier to refer to a given point later. 
- 
-  - How variables/​values from the outer scope are bound. 
-  - How concise it should be / Readability. 
-  - Using a keyword or not. 
-  - Will it ever be able to support statement blocks instead of only a single expression? 
-  - Syntax even aside from the above concerns. 
- 
- 
-===== Binding variables from the outer scope ===== 
- 
-The current proposal binds variables used from the outer scope by value. To address the concern from the community I propose that by default we bind by value and allow opting into binding by reference. Being by-value as the default and opting into by-reference is prudent for two reasons: 
- 
-  - It mimics how we deal with parameters and the return value. By default they are by-value and are changed to pass-by-reference and return-by-reference when the respective ampersands are present. The amended proposal makes this consistent. 
-  - Binding by-reference has a higher performance cost. 
- 
-The details of how to opt-in are detailed in [[#​syntax]]. 
- 
-===== Syntax ==== 
- 
-I am unsure if the other points listed in [[#​concerns|Concerns]] are truly reconcilable. Instead I suggest two syntaxes for us to discuss and choose between. Each has different strengths and weaknesses with regards to points 2-5. 
- 
-Both options support type declarations for parameters and the return type, as well as parameter references and return by-reference. 
- 
-==== Option 1: fn-prefixed ==== 
- 
-This is what current RFC outlines except it has the optional leading ampersand to indicate that bound variables from the outer scope should be done by-reference instead of by-value. The simplified grammar looks like this: 
- 
-    T_FN '​('​ parameter_list '​)'​ T_DOUBLE_ARROW expr 
- 
-This requires a new keyword ''​fn''​ and token ''​T_FN''​. There are a few usages in ''​.phpt''​ tests and I expect it will similarly disrupt test suites of other projects as well. 
- 
-==== Option 2: curly-brace enclosed ==== 
- 
-The simplified grammar looks like this: 
- 
-    '​{'​ '​('​ parameter_list '​)'​ T_DOUBLE_ARROW expr '​}'​ 
-    ​ 
-You may omit the parenthesis around the parameter list when there is only a single variable and neither references nor type information is provided: 
- 
-<​PHP>​{$x => $x + 2}</​PHP>​ 
- 
-To support this syntax the grammar is modified to allow these closures wherever expressions are allowed //except// as a statement because we allow freestanding blocks. This is an acceptable trade-off because such a closure would not have any positive observable effect. 
- 
-This syntax would restrict the ability to add object literals a-la JavaScript. Specifically it would prevent the ability to use arbitrary expressions as object literal keys. 
- 
-==== Option 3 ==== 
- 
-This option is the same as option 1 except that instead of adding a new keyword ''​T_FN''​ we just re-use ''​T_FUNCTION'':​ 
- 
-    T_FUNCTION '​('​ parameter_list '​)'​ T_DOUBLE_ARROW expr 
- 
-===== Examples ===== 
- 
-Here are some snippets from real code-bases converted to use the three syntaxes: 
- 
-<PHP> 
-$s1 = fn($c) => $callable($factory($c),​ $c); 
-$s2 = {$c => $callable($factory($c),​ $c)}; 
-$s3 = function ($c) => $callable($factory($c),​ $c); 
- 
-$this->​existingSchemaPaths = array_filter($paths,​ fn($v) => in_array($v,​ $names)); 
-$this->​existingSchemaPaths = array_filter($paths,​ {$v => in_array($v,​ $names)}); 
-$this->​existingSchemaPaths = array_filter($paths,​ function($v) => in_array($v,​ $names)); 
- 
-function complement_s1(callable $f) { 
-    return fn(...$args) => !$f(...$args);​ 
-} 
-function complement_s2(callable $f) { 
-    return {(...$args) => !$f(...$args)};​ 
-} 
-function complement_s3(callable $f) { 
-    return function(...$args) => !$f(...$args);​ 
-} 
- 
-function reject_s1($collection,​ callable $function) { 
-    return filter($collection,​ fn($value, $key) => !$function($value,​ $key)); 
-} 
-function reject_s2($collection,​ callable $function) { 
-    return filter($collection,​ {($value, $key) => !$function($value,​ $key)}); 
-} 
-function reject_s3($collection,​ callable $function) { 
-    return filter($collection,​ function($value,​ $key) => !$function($value,​ $key)); 
-} 
- 
-// not real code but typical of such 
-$result = Collection::​from([1,​ 2]) 
-    ->​map(fn($v) => $v * 2) 
-    ->​reduce(fn($tmp,​ $v) => $tmp + $v, 0); 
-$result = Collection::​from([1,​ 2]) 
-    ->​map({$v => $v * 2}) 
-    ->​reduce({($tmp,​ $v) => $tmp + $v}, 0); 
-$result = Collection::​from([1,​ 2]) 
-    ->​map(function($v) => $v * 2) 
-    ->​reduce(function($tmp,​ $v) => $tmp + $v, 0); 
-</​PHP>​ 
- 
-I'll point out only one thing and leave the rest to the you to analyze: the closing curly brace ''​}''​ makes it a bit easier to distinguish the end of the expression when the closure is directly passed as a parameter. 
- 
-=== Of references === 
-As an example of all the places references can be used: 
- 
-    &fn &​(&​$x) => function_that_takes_ref_and_returns_ref($x,​ $y) 
-    ^   ^ ^ 
-    |   | \ Parameter is passed by reference 
-    |   \ Function returns by reference 
-    \ Variables bound from the outer-scope are bound by reference 
- 
-Here is the same closure written in the brace-enclosed style: 
- 
-    &{ &​(&​$x) => function_that_takes_ref_and_returns_ref($x,​ $y) } 
-    ^  ^ ^ 
-    |  | \ Parameter is passed by reference 
-    |  \ Function returns by reference 
-    \ Variables bound from the outer-scope are bound by reference 
-    ​ 
-And in the last option: 
- 
-    &​function &​(&​$x) => function_that_takes_ref_and_returns_ref($x,​ $y) } 
-    ^         ^ ^ 
-    |         | \ Parameter is passed by reference 
-    |         \ Function returns by reference 
-    \ Variables bound from the outer-scope are bound by reference 
- 
-[[https://​chat.stackoverflow.com/​users/​4251625/​wes-stark|Wes Stark]] suggests that we move the return-by-reference sigil to behind the parameters where we put return type information:​ 
- 
-    {($x): & => function_that_returns_ref($x,​ $y) } 
-           ^ 
-           \ Function returns by reference 
- 
-This cleans up the potential density if you close by reference and return by reference but means that we have a break in convention. 
- 
-=== Of Nested Closures === 
- 
-Nikita Popov requested that I show an example of nested closures: 
- 
-<PHP> 
-$add2 = fn($x) => fn($y) => $x + $y; 
- 
-$add2 = {$x => {$y => $x + $y}}; 
- 
-$add2 = function($x) => function($y) => $x + $y; 
-</​PHP>​ 
- 
-===== Vote ===== 
- 
-<doodle title="​Preference of Arrow Function Syntax for PHP 7.2" auth="​levim voteType="​single"​ closed="​true">​ 
-   * Prefixed with fn 
-   * Enclosed by curly-braces 
-   * Reuse function 
-   * No Preference 
-</​doodle>​ 
  
rfc/arrow_function_preference.txt · Last modified: 2017/09/22 13:28 (external edit)