rfc:arrow_functions_v2

Differences

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

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Last revisionBoth sides next revision
rfc:arrow_functions_v2 [2019/03/13 15:10] nikicrfc:arrow_functions_v2 [2020/04/18 14:54] – Less party sebastian
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   * Implementation: https://github.com/php/php-src/pull/3941
 +  * Status: Accepted
  
 ===== Introduction ===== ===== Introduction =====
  
-In PHP anonymous functions can be quite verbose even when they only perform a simple operation. +Anonymous functions in PHP can be quite verboseeven when they only perform a simple operation. 
-Partly this is due to a large amount of syntactic boilerplate, and party due to the need to manually+Partly this is due to a large amount of syntactic boilerplate, and partly due to the need to manually
 import used variables. This makes code using simple closures hard to read and understand. This RFC import used variables. This 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 24: Line 24:
  
 The actual <php>$arr[$x]</php> operation performed by the closure is trivial, but is somewhat lost The actual <php>$arr[$x]</php> operation performed by the closure is trivial, but is somewhat lost
-amidst the syntactic boilerplate. Using arrow functions from this RFC this would reduce it to the +amidst the syntactic boilerplate. Arrow functions would reduce the function to the following:
-following:+
  
 <PHP> <PHP>
Line 35: Line 34:
 The question of short closures has been extensively discussed in the past. A previous 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 [[rfc:short_closures|short closures RFC]] went through voting and was declined. This proposal
-tries to address some of the raised concerns with a different choice of syntaxthat is not+tries to address some of the raised concerns with a different choice of syntax that is not
 subject to the limitations of the previous proposal. subject to the 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 72: 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 91: Line 90:
 </PHP> </PHP>
  
-==== Static arrow functions ====+==== $this binding and static arrow functions ====
  
-For the sake of completenessstatic arrow functions are supported using the syntax +Just like normal closures, the ''$this'' variablethe scope and the LSB scope are automatically bound when short closure is created inside a class method. For normal closuresthis can be prevented by prefixing them with ''static''For the sake of completeness this is also supported for arrow functions:
-<php>static fn($x) => $x</php>. A static arrow functionjust like static closure, will not +
-bind the ''$this'' variable.+
  
-Static closures are mainly (exclusively?used to avoid unnecessary cycles involving ''$this'', +<PHP> 
-which may result in delayed GC.+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. 
 + 
 +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 closure. Apart from GC effects, this 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''.
  
 ==== By-value variable binding ==== ==== By-value variable binding ====
Line 146: 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 170: 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 211: 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 218: 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 224: 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 235: Line 247:
     }, 0);     }, 0);
  
-echo $result; //6+echo $result; // 6
  
 // with arrow functions: // with arrow functions:
Line 242: 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 424: 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 446: 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 515: Line 555:
 This syntax is not viable in PHP, because ''[$x]($y)'' is already valid syntax, so this would run This syntax is not viable in PHP, because ''[$x]($y)'' is already valid syntax, so this would run
 into all the same parsing issues. into all the same parsing issues.
 +
 +=== Miscellaneous ===
 +
 +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 Haskell. This 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 528: 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 625: Line 677:
  
 <PHP> <PHP>
 +fn(params) => {
 +    stmt1;
 +    stmt2;
 +    return expr;
 +}
 +// or possibly just
 fn(params) { fn(params) {
     stmt1;     stmt1;
Line 690: 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