rfc:closure_self_reference

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:closure_self_reference [2020/11/18 21:30] – Changed to be less magic. danackrfc:closure_self_reference [2023/06/03 21:00] – typo. danack
Line 1: Line 1:
 ====== PHP RFC: Closure self reference ====== ====== PHP RFC: Closure self reference ======
-  * Version: 0.2 +  * Version: 1.0 
-  * Date: 2020-11-10 +  * Date: 2023-06-03 
-  * Author: Danack +  * Author: Danack, KapitanOczywisty 
-  * Status: Draft+  * Status: Under Discussion
   * First Published at: https://wiki.php.net/rfc/closure_self_reference   * First Published at: https://wiki.php.net/rfc/closure_self_reference
  
Line 29: Line 29:
  
 i.e. a change to the variable outside the closure has modified the behaviour of the closure. That a closure can change behaviour like this violates the [[https://en.wikipedia.org/wiki/Principle_of_least_astonishment|Principle of least astonishment]] i.e. a change to the variable outside the closure has modified the behaviour of the closure. That a closure can change behaviour like this violates the [[https://en.wikipedia.org/wiki/Principle_of_least_astonishment|Principle of least astonishment]]
 +
 +It would be better if it was possible to reference a closure inside itself, without having to `use` an external variable.
  
 ===== Proposal ===== ===== Proposal =====
Line 35: Line 37:
  
 <code php> <code php>
-$fibonacci = function (int $n) as $fibonacci {+$fibonacci = function (int $n) as $fn {
     if ($n === 0) return 0;     if ($n === 0) return 0;
     if ($n === 1) return 1;     if ($n === 1) return 1;
-    return $fibonacci($n-1) + $fibonacci($n-2);+    return $fn($n-1) + $fn($n-2);
 }; };
 +
 +echo $fibonacci(5);
 </code> </code>
 +
 +Or as a short closure, aka arrow function:
 +
 +<code php>
 +$factorial = fn(int $num) as $fn : int => $num > 1 ? $num * $fn($num - 1) : $num;
 +</code>
 +
 +===== Syntax choices =====
 +
 +There are a plethora of possible syntax choices. Those that have been thought about about are:
 +
 +==== A constant ====
 +
 +It would be possible to define a constant that refers to the current closure e.g. __CLOSURE__
 +
 +<code php>
 +$fibonacci = function (int $n) {
 +    if ($n === 0) return 0;
 +    if ($n === 1) return 1;
 +    return __CLOSURE__($n-1) + __CLOSURE__($n-2);
 +};
 +
 +$factorial = fn(int $num): int => $num > 1 ? $num * __CLOSURE__($num - 1) : $num;
 +</code>
 +
 +
 +==== Alias after function return type ====
 +
 +<code php>
 +$fibonacci = function (int $n): int as $fn {
 +    if ($n === 0) return 0;
 +    if ($n === 1) return 1;
 +    return $fn($n-1) + $fn($n-2);
 +};
 +</code>
 +
 +Due to parser limitations, this can't be used for short closures.
 +
 +
 +==== A static function on the closure class ====
 +
 +e.g. Closure::current()
 +
 +<code php>
 +$fibonacci = function (int $n) {
 +    if ($n === 0) return 0;
 +    if ($n === 1) return 1;
 +    return (Closure::current())($n-1) + (Closure::current())($n-2);
 +};
 +
 +$factorial = fn(int $num) as $fn : int => $num > 1 ? $num * (Closure::current())($num - 1) : $num;
 +</code>
 +
 +
 +
 +==== De-anonymize the function ====
 +
 +i.e. allow a variable name to be used after the keyword function before the parentheses containing the parameters:
 +
 +<code php>
 +$fibonacci = function $fn(int $n) {
 +    if ($n === 0) return 0;
 +    if ($n === 1) return 1;
 +    return $fn($n-1) + $fn($n-2);
 +};
 +
 +$factorial = fn $fn(int $num) as $fn : int => $num > 1 ? $num * $fn($num - 1) : $num;
 +</code>
 +
 +This has a large aesthetic downside of appearing to create the closure variable in the scope that the closure is declared in, rather than internal to the closure scope.
 +
 +
 +==== Alias after use variables ====
 +
 +<code php>
 +$fibonacci_offset = function (int $n) use ($offset) as $fn: int {
 +    if ($n === 0) return $offset;
 +    if ($n === 1) return 1;
 +    return $fn($n-1) + $fn($n-2);
 +};
 +</code>
 +
 +<code php>
 +$factorial = fn(int $num) as $fn: int => $num > 1 ? $num * $fn($num - 1) : $num;
 +</code>
 +
 +
 +
 +==== Alias immediately after function parameters ====
 +<code php>
 +$fibonacci_offset = function (int $n) as $fn use($offset): int {
 +    if ($n === 0) return $offset;
 +    if ($n === 1) return 1;
 +    return $fn($n-1) + $fn($n-2);
 +};
 +</code>
 +
 +<code php>
 +$factorial = fn(int $num) as $fn : int => $num > 1 ? $num * $fn($num - 1) : $num;
 +</code>
 +
 +
 +===== Syntax choice evaluation =====
 +
 +Of the syntaxes considered, the following syntaxes are excluded for the reasons listed:
 +
 +* A constant. Although this could work it has multiple aesthetic downsides of being verbose, ugly, and just not very language-y.
 +
 +* Alias after function return type. Can't be used for short closures due to parser limitations.
 +
 +* A static function on the closure class. Just not very language-y, quite verbose due to the extra ()'s needed
 +
 +* De-anonymize the function. This has a large aesthetic downside of appearing to create the closure variable in the scope that the closure is declared in, rather than internal to the closure scope.
 +
 +Which leaves the 'Alias after use variables' and 'Alias immediately after function parameters' as the acceptable options.
 +
 +As the authors find the 'Alias immediately after function parameters' easiest to read, that is the syntax that has been chosen.
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
Line 48: Line 169:
 ===== Proposed PHP Versions ===== ===== Proposed PHP Versions =====
  
-8.1+8.3
  
 ===== RFC Impact ===== ===== RFC Impact =====
Line 67: Line 188:
  
 ===== Implementation ===== ===== Implementation =====
-None yet.+https://github.com/php/php-src/pull/11118
  
 ===== References ===== ===== References =====
rfc/closure_self_reference.txt · Last modified: 2023/06/03 21:01 by danack