rfc:generators
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
rfc:generators [2012/08/08 20:12] – nikic | rfc:generators [2017/09/22 13:28] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== Request for Comments: Generators ====== | ====== Request for Comments: Generators ====== | ||
* Date: 2012-06-05 | * Date: 2012-06-05 | ||
- | * Author: | + | * Author: |
- | * Status: | + | * Status: |
===== Introduction ===== | ===== Introduction ===== | ||
Line 189: | Line 189: | ||
| | ||
mixed send(mixed $value); | mixed send(mixed $value); | ||
- | | + | |
} | } | ||
</ | </ | ||
Line 223: | Line 223: | ||
Apart from the above the '' | Apart from the above the '' | ||
- | * '' | + | * '' |
* '' | * '' | ||
* '' | * '' | ||
Line 229: | Line 229: | ||
* '' | * '' | ||
* '' | * '' | ||
- | * '' | + | * '' |
- | ==== Yield keyword | + | ==== Yield syntax |
The newly introduced '' | The newly introduced '' | ||
Line 280: | Line 280: | ||
Python also has parentheses requirements for expression-use of '' | Python also has parentheses requirements for expression-use of '' | ||
+ | |||
+ | See also the [[# | ||
==== Yielding keys ==== | ==== Yielding keys ==== | ||
Line 392: | Line 394: | ||
$logger-> | $logger-> | ||
$logger-> | $logger-> | ||
+ | </ | ||
+ | |||
+ | ==== Throwing into the generator ==== | ||
+ | |||
+ | Exceptions can be thrown into the generator using the '' | ||
+ | context and then resume the generator. It is roughly equivalent to replacing the current '' | ||
+ | resuming then. If the generator is already closed the exception will be thrown in the callers context instead (which is equivalent to replacing | ||
+ | the '' | ||
+ | other exception is thrown). | ||
+ | |||
+ | An example of the functionality: | ||
+ | |||
+ | <code php> | ||
+ | function gen() { | ||
+ | echo " | ||
+ | try { | ||
+ | yield; | ||
+ | } catch (Exception $e) { | ||
+ | echo " | ||
+ | } | ||
+ | echo " | ||
+ | } | ||
+ | |||
+ | $gen = gen(); | ||
+ | $gen-> | ||
+ | $gen-> | ||
+ | // and " | ||
</ | </ | ||
Line 412: | Line 441: | ||
Here rewinding would simply result in an empty iterator as the result set is already depleted. | Here rewinding would simply result in an empty iterator as the result set is already depleted. | ||
- | One solution thus could be to allow explicitly marking | + | For the above reasons |
- | which makes a generator | + | |
<code php> | <code php> | ||
- | $gen = rewindable(gen()); | + | $gen = createSomeGenerator(); |
- | </ | + | |
- | + | ||
- | This function is actually already implementable in userland code (see " | + | |
- | ==== Cloning a generator | + | // the rewind() call foreach is doing here is okay, because |
+ | // the generator | ||
+ | foreach ($gen as $val) { ... } | ||
- | Generators can be cloned, thus leaving two independent '' | + | // the rewind() |
- | used to create the aforementioned '' | + | // throws an exception |
- | + | foreach | |
- | <code php> | + | |
- | class RewindableGenerator implements Iterator { | + | |
- | protected $original; | + | |
- | protected $current; | + | |
- | + | ||
- | public function __construct(Generator $generator) { | + | |
- | $this-> | + | |
- | $this-> | + | |
- | } | + | |
- | + | ||
- | public function | + | |
- | if ($this-> | + | |
- | | + | |
- | $this-> | + | |
- | } | + | |
- | + | ||
- | public function valid() { | + | |
- | if (!$this-> | + | |
- | return $this-> | + | |
- | } | + | |
- | + | ||
- | public function current() { | + | |
- | if (!$this-> | + | |
- | return $this-> | + | |
- | } | + | |
- | + | ||
- | public function key() { | + | |
- | if (!$this-> | + | |
- | return $this-> | + | |
- | } | + | |
- | + | ||
- | public function next() { | + | |
- | if (!$this-> | + | |
- | $this-> | + | |
- | } | + | |
- | + | ||
- | public function send($value) { | + | |
- | if (!$this-> | + | |
- | return $this-> | + | |
- | } | + | |
- | + | ||
- | public function close() { | + | |
- | $this-> | + | |
- | if ($this-> | + | |
- | $this-> | + | |
- | } | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | function rewindable(Generator $generator) { | + | |
- | return new RewindableGenerator($generator); | + | |
- | } | + | |
</ | </ | ||
- | It can be then used as follows: | + | So basically calling '' |
- | <code php> | + | ==== Cloning a generator ==== |
- | function xrange($start, | + | |
- | for ($i = $start; $i <= $end; $i += $step) { | + | |
- | yield $i; | + | |
- | } | + | |
- | } | + | |
- | $range = rewindable(xrange(0, | + | Generators cannot be cloned. |
- | foreach ($range as $i) { | + | |
- | echo $i, " | + | |
- | } | + | |
- | foreach ($range as $i) { | + | |
- | echo $i, " | + | |
- | } | + | |
- | </ | + | |
- | This will correctly output | + | Support for cloning was included in the initial version, but removed in PHP 5.5 Beta 3 due to implementational difficulties, |
==== Closing a generator ==== | ==== Closing a generator ==== | ||
Line 505: | Line 468: | ||
'' | '' | ||
- | A generator can be closed in three ways: | + | A generator can be closed in two ways: |
- | * Explicitly calling the '' | + | * Reaching a '' |
* Removing all references to the generator object. In this case the generator will be closed as part of the garbage collection process. | * Removing all references to the generator object. In this case the generator will be closed as part of the garbage collection process. | ||
- | * Reaching a '' | + | |
+ | If the generator contains (relevant) | ||
+ | allowed to use '' | ||
The following resources are destructed while closing a generator: | The following resources are destructed while closing a generator: | ||
Line 524: | Line 489: | ||
Currently it can happen that temporary variables are not cleaned up properly in edge-case situations. Exceptions are also subject to | Currently it can happen that temporary variables are not cleaned up properly in edge-case situations. Exceptions are also subject to | ||
this problem: https:// | this problem: https:// | ||
- | |||
- | A generator cannot be closed while it is running (e.g. if '' | ||
- | is thrown and the call is ignored. | ||
- | |||
- | In Python generators are closed by throwing a '' | ||
- | |||
- | The exception gives the generator function a chance to clean up any resources it uses: | ||
- | |||
- | <code php> | ||
- | function gen() { | ||
- | $lock = getLock(); | ||
- | try { | ||
- | // do some stuff | ||
- | } catch (GeneratorExitException $e) { | ||
- | releaseLock($lock); | ||
- | throw $e; | ||
- | } | ||
- | releaseLock($lock); | ||
- | } | ||
- | </ | ||
- | |||
- | As you can see the exception allows the generator to clean up. Also note that the exception has to be rethrown. This way it is ensured that | ||
- | the generator does not simply resume execution, but really does terminate. | ||
- | |||
- | As PHP does not support '' | ||
- | (e.g. destructors) this behavior is not implemented in PHP. | ||
==== Error conditions ==== | ==== Error conditions ==== | ||
Line 555: | Line 494: | ||
This is a list of generators-related error conditions: | This is a list of generators-related error conditions: | ||
+ | * Using '' | ||
+ | * Using '' | ||
* Manual construction of '' | * Manual construction of '' | ||
- | * Closing a generator while it is running: '' | ||
* Yielding a key that isn't an integer or a key: '' | * Yielding a key that isn't an integer or a key: '' | ||
- | * Trying to iterate a non-ref generator by-ref: '' | + | * Trying to iterate a non-ref generator by-ref: '' |
- | * Using '' | + | * Trying to traverse an already closed generator: '' |
- | * Using '' | + | * Trying to rewind |
* Yielding a temp/const value by-ref: '' | * Yielding a temp/const value by-ref: '' | ||
* Yielding a string offset by-ref: '' | * Yielding a string offset by-ref: '' | ||
Line 566: | Line 506: | ||
This list might not be exhaustive. | This list might not be exhaustive. | ||
- | |||
- | ===== Patch ===== | ||
- | |||
- | A working, but not yet complete implementation can be found at https:// | ||
===== Performance ===== | ===== Performance ===== | ||
Line 587: | Line 523: | ||
The tests were run on a Ubuntu VM, so I'm not exactly sure how representative they are. | The tests were run on a Ubuntu VM, so I'm not exactly sure how representative they are. | ||
- | ===== Why not just use callback functions? | + | ===== Some points from the discussion ===== |
+ | |||
+ | ==== Why not just use callback functions? ==== | ||
A question that has come up a few times during discussion: Why not use callback functions, instead of generators? For example the above '' | A question that has come up a few times during discussion: Why not use callback functions, instead of generators? For example the above '' | ||
Line 659: | Line 597: | ||
generators solve this problem elegantly, because they maintain state implicitly, in the execution state. | generators solve this problem elegantly, because they maintain state implicitly, in the execution state. | ||
- | ===== TODO ===== | + | ==== Alternative yield syntax considerations |
+ | |||
+ | Andrew proposed to use a function-like syntax for '' | ||
+ | |||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | The main advantage of this syntax is that it would avoid the strange parentheses requirements for the '' | ||
+ | |||
+ | One of the main issues with the pseudo-function syntax is that it makes the semantics of '' | ||
+ | syntax. Both are very similar in a function, so it is desirable to keep them similar in syntax too. | ||
+ | |||
+ | Generally PHP uses the '' | ||
+ | '' | ||
+ | wouldn' | ||
+ | |||
+ | As '' | ||
+ | '' | ||
+ | |||
+ | So the function-like '' | ||
+ | |||
+ | ===== Patch ===== | ||
+ | |||
+ | The current implementation can be found in this branch: https:// | ||
+ | |||
+ | I also created a PR so that the diff can be viewed more easily: https:// | ||
+ | |||
+ | ===== Vote ===== | ||
- | * Decide on whether to implement the '' | + | <doodle title=" |
- | * Implement '' | + | * Yes |
+ | | ||
+ | </ | ||
===== Further resources ===== | ===== Further resources ===== |
rfc/generators.txt · Last modified: 2017/09/22 13:28 by 127.0.0.1