rfc:arrow_functions_v2

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_v2 [2019/03/12 16:40]
nikic
rfc:arrow_functions_v2 [2020/04/18 14:54]
sebastian Less party
Line 4: Line 4:
   * Author: Levi Morrison <levim@php.net>   * Author: Levi Morrison <levim@php.net>
   * Author: Bob Weinand <bwoebi@php.net>   * Author: Bob Weinand <bwoebi@php.net>
-  * Target PHP version: PHP 7.4 +  * Target version: PHP 7.4 
-  * Status: Draft+  * Implementation: https://github.com/php/php-src/pull/3941 
 +  * Status: Accepted
  
 ===== Introduction ===== ===== Introduction =====
  
-Anonymous functions and closures can be verbose even though they perform only a simple operation. +Anonymous functions in PHP can be quite verboseeven when they only perform a simple operation. 
-Additionally, importing variables into the closure's scope is manual and painful overhead for +Partly this is due to large amount of syntactic boilerplate, and partly due to the need to manually 
-single-expression closuresIn practice these single-expression closures are common. This RFC+import used variablesThis makes code using simple closures hard to read and understand. This RFC
 proposes a more concise syntax for this pattern. proposes a more concise syntax for this pattern.
  
Line 22: Line 23:
 </PHP> </PHP>
  
-The closure performs a single operation <php>$arr[$x]</php> and is 8 characters but requires 30 +The actual <php>$arr[$x]</php> operation performed by the closure is trivial, but is somewhat lost 
-other characters (excluding whitespace). This means that roughly 79% of the closure's code is +amidst the syntactic boilerplate. Arrow functions would reduce the function to the following:
-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 implicitlyUsing arrow functions from this RFC this would reduce +
-to the following:+
  
 <PHP> <PHP>
Line 35: Line 32:
 </PHP> </PHP>
  
-This reduces the amount of boilerplate from 30 characters down to 8. +The question of short closures has been extensively discussed in the past. A previous 
- +[[rfc:short_closures|short closures RFC]] went through voting and was declined. This proposal 
-A previous [[rfc:short_closures|short closures RFC]] has been declined. This proposal tries to +tries to address some of the raised concerns with a different choice of syntax that is not 
-address some of the raised concerns with a different choice of syntaxthat is not subject to the +subject to the limitations of the previous proposal.
-limitations of the previous proposal.+
  
 Additionally, this RFC includes a lengthy discussion of different syntax alternatives as well as Additionally, this RFC includes a lengthy discussion of different syntax alternatives as well as
 binding semantics. Unfortunately short closures are a topic where we're unlikely to find a "perfect" binding semantics. Unfortunately short closures are a topic where we're unlikely to find a "perfect"
-solution, due to significant constraints on the syntax and implementation. This proposal makes a+solution, due to significant constraints on the syntax and implementation. This proposal makes the
 choice that we consider "least bad". Short closures are critically overdue, and at some point we'll choice that we consider "least bad". Short closures are critically overdue, and at some point we'll
 have to make a compromise here, rather than shelving the topic for another few years. have to make a compromise here, rather than shelving the topic for another few years.
Line 75: Line 71:
 </PHP> </PHP>
  
-Here the outer function captures ''$x''. The inner function then also captures ''$x'' from the outer +Here the outer function captures ''$z''. The inner function then also captures ''$z'' from the outer 
-function. The overall effect is that the ''$z'' from the outer scope becomes available inside the+function. The overall effect is that ''$z'' from the outer scope becomes available in the
 inner function. inner function.
  
Line 94: Line 90:
 </PHP> </PHP>
  
-==== Static arrow functions ====+==== $this binding and static arrow functions ==== 
 + 
 +Just like normal closures, the ''$this'' variable, the scope and the LSB scope are automatically bound when a short closure is created inside a class method. For normal closures, this can be prevented by prefixing them with ''static''. For the sake of completeness this is also supported for arrow functions: 
 + 
 +<PHP> 
 +class Test { 
 +    public function method() { 
 +        $fn = fn() => var_dump($this); 
 +        $fn(); // object(Test)#1 { ... } 
 +         
 +        $fn = static fn() => var_dump($this); 
 +        $fn(); // Error: Using $this when not in object context 
 +    } 
 +
 +</PHP> 
 + 
 +Static closures are rarely used: They're mainly used to prevent ''$this'' cycles, which make GC behavior less predictable. Most code need not concern itself with this.
  
-For the sake of completeness, static arrow functions are supported using the syntax +It has been suggested that we could use this opportunity to change the ''$this'' binding semantics towards only binding ''$this'' if it is actually used inside the closureApart from GC effectsthis would result in the same behavior. Unfortunately PHP has some implicit uses of ''$this''For example ''Foo::bar()'' calls may inherit ''$this'' if it is compatible with the ''Foo'' scope. We could only carry out a conservative analysis of potential ''$this'' use, which would be unpredictable from a user perspective. As such, we prefer to keep the existing behavior of always binding ''$this''.
-<php>static fn($x) => $x</php>A static arrow functionjust like a static closure, will not +
-bind the ''$this'' variableI believe the only way in which static closures are used in practice +
-is to avoid GC cycles and make destruction behavior more predictable.+
  
 ==== By-value variable binding ==== ==== By-value variable binding ====
Line 135: Line 144:
  
 Here ''$matches'' is populated by ''preg_match()'' and needn't exist prior to the call. We would Here ''$matches'' is populated by ''preg_match()'' and needn't exist prior to the call. We would
-not want to generate a spurious undefined variable noticed in this case.+not want to generate a spurious undefined variable notice in this case.
  
 Finally, the automatic binding mechanism only considers variables that are used literally. That is, Finally, the automatic binding mechanism only considers variables that are used literally. That is,
Line 147: Line 156:
 </PHP> </PHP>
  
-Support for this could be easily added by using a more general binding mechanism (bind everything +Support for this could be added by using a more general binding mechanism (bind everything 
-rather than binding what is used) when variable variables are encountered. It's excluded here +rather than binding what is used) when variable variables are encountered. It's excluded here because 
-because supporting this is a waste of opcodes, but if people don't like that, it can be supported.+it seems like an entirely unnecessary complication of the implementation, but it can be supported if 
 +people consider it necessary.
  
 ==== Precedence ==== ==== Precedence ====
Line 171: Line 181:
 [[https://gist.github.com/morrisonlevi/473a7e0cb6e59c830224b1c71b8da28c|The gist]] provides more [[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 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.+except one case where it is a namespace segment. (The namespace use happens to be in my own library, 
 +and I'm happy to rename it.)
  
 ===== Examples ===== ===== Examples =====
Line 212: Line 223:
 <PHP> <PHP>
 function complement(callable $f) { function complement(callable $f) {
-    return function (... $args) use ($f) { +    return function (...$args) use ($f) { 
-        return !$f(... $args);+        return !$f(...$args);
     };     };
 } }
Line 219: Line 230:
 // with arrow function: // with arrow function:
 function complement(callable $f) { function complement(callable $f) {
-    return fn(... $args) => !$f(... $args);+    return fn(...$args) => !$f(...$args);
 } }
 </PHP> </PHP>
Line 225: Line 236:
 ----- -----
  
-The following example was given to me by [[https://gist.github.com/tpunt/b4f9bf30f43b9e148b73ce18245ab472|tpunt]]:+The following example was provided by [[https://gist.github.com/tpunt/b4f9bf30f43b9e148b73ce18245ab472|tpunt]]:
  
 <PHP> <PHP>
Line 236: Line 247:
     }, 0);     }, 0);
  
-echo $result; //6+echo $result; // 6
  
 // with arrow functions: // with arrow functions:
Line 243: Line 254:
     ->reduce(fn($tmp, $v) => $tmp + $v, 0);     ->reduce(fn($tmp, $v) => $tmp + $v, 0);
  
-echo $result; //6+echo $result; // 6
 </PHP> </PHP>
  
 ===== Vote ===== ===== Vote =====
  
-Simple yes/no vote.+Voting started 2019-04-17 and ends 2019-05-01. A 2/3 majority is required. 
 + 
 +<doodle title="Add arrow functions as described in PHP 7.4?" auth="nikic" voteType="single" closed="true"> 
 +   * Yes 
 +   * No 
 +</doodle>
  
 ===== Discussion ===== ===== Discussion =====
Line 294: Line 310:
 </PHP> </PHP>
  
-Unfortunately, the ''%%$x => $y%%'' syntax is also subject to the limitation that are described in the following section, which are ultimately much more problematic.+Unfortunately, the ''%%$x => $y%%'' syntax is also subject to the limitations that are described in the following section, which are ultimately much more problematic.
  
 === ($x) ==> $x * $y === === ($x) ==> $x * $y ===
Line 425: Line 441:
 ~($x) => $x * $y ~($x) => $x * $y
 @($x) => $x * $y @($x) => $x * $y
 +
 +// Not possible, because _() is a valid function name, used as an alias for gettext()
 +_($x) => $x * $y
 </PHP> </PHP>
  
Line 447: Line 466:
 It would be possible to resolve this ambiguity by lexing namespaced names as a single token It would be possible to resolve this ambiguity by lexing namespaced names as a single token
 (removing support for whitespace inside them). This would, however, be a breaking change. (removing support for whitespace inside them). This would, however, be a breaking change.
 +
 +=== Using -> and --> as arrows ===
 +
 +As an alternative to ''%%=>%%'' the use of ''%%->%%'' and ''%%-->%%'' has been suggested. Any arrow syntax without a leading sigil would still be subject to the issues in the previous section, but these two in particular also conflict with existing syntax: ''%%->%%'' is already used for property access:
 +
 +<PHP>
 +($x) -> $x
 +// already valid, more typically written as:
 +$x->{$x}
 +</PHP>
 +
 +''%%-->%%'' is a combination of The post-decrement operator ''%%--%%'' and the greater-than operator ''>'':
 +
 +<PHP>
 +$x --> $x
 +// already valid, more typically written as:
 +$x-- > $x
 +</PHP>
 +
 +''%%-->%%'' would be valid when restricted to the form that uses parentheses, because ''%%($x)--%%'' is not legal code right now. Both arrows would be possible in conjunction with a leading symbol, but at that point any ambiguity is already resolved by the leading symbol and we may as well use ''%%=>%%''.
  
 === Different parameter list separators === === Different parameter list separators ===
Line 517: Line 556:
 into all the same parsing issues. into all the same parsing issues.
  
-=== Other ideas? ===+=== Miscellaneous ===
  
-Happy to add more to this list.+It has been suggested to use the ''%%\param_list => expr%%'' syntax (without wrapping the parameters in parentheses), which is very close to the syntax used by HaskellThis syntax is ambiguous, because the ''\'' may also be part of a fully qualified type name: 
 + 
 +<PHP> 
 +[\T &$x => $y] 
 +// could be 
 +[\(T &$x) => $y)] 
 +// or 
 +[(\T & $x) => $y] 
 +</PHP>
  
 ==== Binding behavior ==== ==== Binding behavior ====
Line 533: Line 580:
 <PHP> <PHP>
 $x = 1; $x = 1;
-$fn = () => $x++;+$fn = fn() => $x++;
 $fn(); $fn();
 var_dump($x); // By-value: 1 var_dump($x); // By-value: 1
Line 546: Line 593:
 bindings, due to two main issues: The first is that by-reference bindings have a performance cost, bindings, due to two main issues: The first is that by-reference bindings have a performance cost,
 because they require the creation of reference wrapper, and their subsequent dereferencing. It because they require the creation of reference wrapper, and their subsequent dereferencing. It
-would be rather unfortunate if the choice between using an array function and using the full+would be rather unfortunate if the choice between using an arrow function and using the full
 closure syntax would also have to take into account their different performance characteristics. closure syntax would also have to take into account their different performance characteristics.
  
Line 630: Line 677:
  
 <PHP> <PHP>
 +fn(params) => {
 +    stmt1;
 +    stmt2;
 +    return expr;
 +}
 +// or possibly just
 fn(params) { fn(params) {
     stmt1;     stmt1;
Line 674: Line 727:
 </PHP> </PHP>
  
-In this example ''$b'' is still implicitly used by-value, but ''$a'' is explicitly used by-value. +In this example ''$b'' is still implicitly used by-value, but ''$a'' is explicitly used 
-However, this syntax may be confusing as it is very close to the normal closure syntax, which +by-reference. However, this syntax may be confusing as it is very close to the normal closure 
-would **not** implicitly bind ''$b''.+syntax, which would **not** implicitly bind ''$b''.
  
 ==== Allow arrow notation for real functions ==== ==== Allow arrow notation for real functions ====
Line 695: Line 748:
 There are some possible variations of this, e.g. allow ''%%=>%%'' but not ''fn''. There are some possible variations of this, e.g. allow ''%%=>%%'' but not ''fn''.
  
 +===== Changelog =====
 +
 +  * 2019-03-14: Clarify $this binding and explain why we're sticking with always-bind behavior.
 +  * 2019-03-14: Mention ''%%->%%'', ''%%-->%%'', ''_()'' and ''\$x => $x''.
rfc/arrow_functions_v2.txt · Last modified: 2020/08/01 23:53 by carusogabriel