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
rfc:closure_self_reference [2020/11/18 21:30] – Changed to be less magic. danackrfc:closure_self_reference [2023/06/03 21:01] (current) – 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): 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): 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.1605735052.txt.gz · Last modified: 2020/11/18 21:30 by danack