rfc:closure_self_reference
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
rfc:closure_self_reference [2020/11/18 21:30] – Changed to be less magic. danack | rfc: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: | + | * Version: |
- | * Date: 2020-11-10 | + | * Date: 2023-06-03 |
- | * Author: Danack | + | * Author: Danack, KapitanOczywisty |
- | * Status: | + | * Status: |
* First Published at: https:// | * First Published at: https:// | ||
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:// | 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:// | ||
+ | |||
+ | 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); | ||
</ | </ | ||
+ | |||
+ | Or as a short closure, aka arrow function: | ||
+ | |||
+ | <code php> | ||
+ | $factorial = fn(int $num) as $fn : int => $num > 1 ? $num * $fn($num - 1) : $num; | ||
+ | </ | ||
+ | |||
+ | ===== 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; | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== 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); | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | Due to parser limitations, | ||
+ | |||
+ | |||
+ | ==== A static function on the closure class ==== | ||
+ | |||
+ | e.g. Closure:: | ||
+ | |||
+ | <code php> | ||
+ | $fibonacci = function (int $n) { | ||
+ | if ($n === 0) return 0; | ||
+ | if ($n === 1) return 1; | ||
+ | return (Closure:: | ||
+ | }; | ||
+ | |||
+ | $factorial = fn(int $num) as $fn : int => $num > 1 ? $num * (Closure:: | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ==== 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; | ||
+ | </ | ||
+ | |||
+ | 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 php> | ||
+ | $factorial = fn(int $num): int => $num > 1 ? $num * $fn($num - 1) : $num; | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ==== Alias immediately after function parameters ==== | ||
+ | <code php> | ||
+ | $fibonacci_offset = function (int $n) as $fn use($offset): | ||
+ | if ($n === 0) return $offset; | ||
+ | if ($n === 1) return 1; | ||
+ | return $fn($n-1) + $fn($n-2); | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | <code php> | ||
+ | $factorial = fn(int $num) as $fn : int => $num > 1 ? $num * $fn($num - 1) : $num; | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== 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' | ||
+ | |||
+ | As the authors find the 'Alias immediately after function parameters' | ||
===== 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:// |
===== References ===== | ===== References ===== |
rfc/closure_self_reference.1605735052.txt.gz · Last modified: 2020/11/18 21:30 by danack