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/10 21:12] – FIx code blocks crell | 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: 1.0 |
- | * Date: 2020-11-10 | + | * Date: 2023-06-03 |
- | * Author: Danack | + | * Author: Danack, KapitanOczywisty |
- | * Status: | + | * Status: |
* First Published at: https:// | * First Published at: https:// | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | Currently, the main way used to call a closure from within that closure, is to bind a variable by reference into the closure, when the closure is created. | + | Currently, the main way used to call a closure from within that closure, is to bind a variable by reference into the closure, when the closure is created. |
However this can lead to shenanigans: | However this can lead to shenanigans: | ||
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 ===== | ||
- | Similar to how $this is defined automatically inside | + | Allow closures |
+ | |||
+ | <code php> | ||
+ | $fibonacci = function (int $n) as $fn { | ||
+ | if ($n === 0) return 0; | ||
+ | if ($n === 1) return 1; | ||
+ | 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 ==== | ||
- | A closure | + | It would be possible to define a constant |
<code php> | <code php> | ||
Line 40: | Line 64: | ||
if ($n === 0) return 0; | if ($n === 0) return 0; | ||
if ($n === 1) return 1; | if ($n === 1) return 1; | ||
- | return | + | return |
}; | }; | ||
+ | |||
+ | $factorial = fn(int $num): int => $num > 1 ? $num * __CLOSURE__($num - 1) : $num; | ||
</ | </ | ||
- | Any use of $lambda outside of a closure would give an error, similar to how using $this outside of a method of a class instance is not allowed. | + | |
+ | ==== Alias after function return type ==== | ||
<code php> | <code php> | ||
- | function | + | $fibonacci = function (int $n): int as $fn { |
- | $lambda(); | + | |
- | } | + | |
- | foo(); | + | |
- | // Fatal error: Using $lambda when not in closure context in %s | + | }; |
</ | </ | ||
+ | Due to parser limitations, | ||
- | ===== BC concerns ===== | ||
- | Although this proposal wouldn' | + | ==== A static function on the closure class ==== |
- | Because of this it probably makes sense to break the change into two steps: | + | e.g. Closure::current() |
- | * In one release emit a deprecation notice for any use of a variable named ' | + | <code php> |
- | * In the next release add the ability to use $lambda inside a closure, with the meaning of ' | + | $fibonacci = function (int $n) { |
+ | if ($n === 0) return 0; | ||
+ | if ($n === 1) return 1; | ||
+ | return (Closure::current())($n-1) + (Closure:: | ||
+ | }; | ||
- | ==== Why the name '$lambda' | + | $factorial |
+ | </ | ||
- | Seems a reasonable choice as: | ||
- | * People who don't know what a lambda is are unlikely to use that name by accident. | + | |
- | * People who do know what a lambda is are likely | + | ==== De-anonymize the function ==== |
- | * I find it a a fun word to say. | + | |
+ | i.e. allow a variable | ||
<code php> | <code php> | ||
- | $closure | + | $fibonacci |
- | // Fatal error: Using $closure when not in closure context in %s | + | if ($n === 0) return 0; |
- | // Whoever wrote this code is likely to be confused. | + | 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> | <code php> | ||
- | $lambda | + | $fibonacci_offset |
- | // Fatal error: Using $lambda when not in closure context in %s | + | if ($n === 0) return $offset; |
- | // Whoever wrote this code has a reasonable chance of understanding the issue. | + | if ($n === 1) return 1; |
+ | | ||
+ | }; | ||
</ | </ | ||
- | ===== Backward Incompatible Changes ===== | + | <code php> |
+ | $factorial | ||
+ | </ | ||
- | Use of any variable named $lambda would issue a deprecation notice on the version where a deprecation notice is added. | ||
- | Any use of $lambda would have to be inside a closure for versions after it is added. | ||
- | ===== Proposed PHP Versions | + | ==== Alias immediately after function parameters |
+ | <code php> | ||
+ | $fibonacci_offset | ||
+ | if ($n === 0) return $offset; | ||
+ | if ($n === 1) return 1; | ||
+ | return $fn($n-1) + $fn($n-2); | ||
+ | }; | ||
+ | </ | ||
- | The voting choices will allow people to choose between: | + | <code php> |
+ | $factorial = fn(int $num) as $fn : int => $num > 1 ? $num * $fn($num - 1) : $num; | ||
+ | </ | ||
- | Either: Deprecate use of $lambda in 8.1 and introduce $lambda to be available inside closures for 8.2 | ||
- | Or: Deprecate use of $lambda in the last planned minor release of PHP 8 and introduce $lambda to be available inside closures for PHP 9.0 | + | ===== Syntax choice evaluation ===== |
- | or rejecting | + | 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 ===== | ||
+ | |||
+ | None known. | ||
+ | |||
+ | ===== Proposed PHP Versions ===== | ||
+ | |||
+ | 8.3 | ||
===== RFC Impact ===== | ===== RFC Impact ===== | ||
Line 107: | Line 178: | ||
===== Future Scope ===== | ===== Future Scope ===== | ||
- | ==== Why not make $lambda usable in all functions, and just refer to the current function/ | + | ===== Questions ===== |
- | + | ||
- | Probably better to just error...unless someone can think of good reasons either way. TBH, I'd probably prefer it to error, to prevent accidental usage. | + | |
===== Proposed Voting Choices ===== | ===== Proposed Voting Choices ===== | ||
- | ==== Vote 1 ==== | + | Accept this RFC and make it possible to reference |
- | + | ||
- | * Accept this RFC, give a deprecation notice for any use of a variable called ' | + | |
- | * Reject this RFC. | + | |
- | + | ||
- | ==== Vote 2 ==== | + | |
- | * Accept this RFC, but at versions PHP 8.x (where x is the last planned minor version for PHP 8) for deprecation and adding | + | |
- | * Reject this RFC. | + | |
- | + | ||
- | If the result of 'vote 1' is to accept the RFC, then vote 2 is moot. | + | |
===== Patches and Tests ===== | ===== Patches and Tests ===== | ||
Line 128: | Line 188: | ||
===== Implementation ===== | ===== Implementation ===== | ||
- | None yet. | + | https:// |
===== References ===== | ===== References ===== |
rfc/closure_self_reference.txt · Last modified: 2023/06/03 21:01 by danack