rfc:generators

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:generators [2012/08/25 16:06] – Start vote nikicrfc: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: Niktia Popov <nikic@php.net> +  * Author: Nikita Popov <nikic@php.net> 
-  * Status: In Voting+  * Status: Implemented
  
 ===== Introduction ===== ===== Introduction =====
Line 189: Line 189:
          
     mixed send(mixed $value);     mixed send(mixed $value);
 +    mixed throw(Exception $exception);
 } }
 </code> </code>
Line 222: Line 223:
 Apart from the above the ''Generator'' methods behave as follows: Apart from the above the ''Generator'' methods behave as follows:
  
-  * ''rewind'': Throws an exception is the generator is currently after the first yield. (More in the "Rewinding a generator" section.)+  * ''rewind'': Throws an exception if the generator is currently after the first yield. (More in the "Rewinding a generator" section.)
   * ''valid'': Returns ''false'' if the generator has been closed, ''true'' otherwise. (More in the "Closing a generator" section.)   * ''valid'': Returns ''false'' if the generator has been closed, ''true'' otherwise. (More in the "Closing a generator" section.)
   * ''current'': Returns whatever was passed to ''yield'' or ''null'' if nothing was passed or the generator is already closed.   * ''current'': Returns whatever was passed to ''yield'' or ''null'' if nothing was passed or the generator is already closed.
Line 228: Line 229:
   * ''next'': Resumes the generator (unless the generator is already closed).   * ''next'': Resumes the generator (unless the generator is already closed).
   * ''send'': Sets the return value of the ''yield'' expression and resumes the generator (unless the generator is already closed). (More in the "Sending values" section.)   * ''send'': Sets the return value of the ''yield'' expression and resumes the generator (unless the generator is already closed). (More in the "Sending values" section.)
 +  * ''throw'': Throws an exception at the current suspension point in the generator. (More in the "Throwing into the generator" section.)
  
 ==== Yield syntax ==== ==== Yield syntax ====
Line 392: Line 394:
 $logger->send('Foo'); $logger->send('Foo');
 $logger->send('Bar'); $logger->send('Bar');
 +</code>
 +
 +==== Throwing into the generator ====
 +
 +Exceptions can be thrown into the generator using the ''Generator::throw()'' method. This will throw an exception in the generator's execution
 +context and then resume the generator. It is roughly equivalent to replacing the current ''yield'' expression with a ''throw'' statement and
 +resuming then. If the generator is already closed the exception will be thrown in the callers context instead (which is equivalent to replacing
 +the ''throw()'' call with a ''throw'' statement). The ''throw()'' method will return the next yielded value (if the exception is caught and no
 +other exception is thrown).
 +
 +An example of the functionality:
 +
 +<code php>
 +function gen() {
 +    echo "Foo\n";
 +    try {
 +        yield;
 +    } catch (Exception $e) {
 +        echo "Exception: {$e->getMessage()}\n";
 +    }
 +    echo "Bar\n";
 +}
 +
 +$gen = gen();
 +$gen->rewind();                     // echos "Foo"
 +$gen->throw(new Exception('Test')); // echos "Exception: Test"
 +                                    // and "Bar"
 </code> </code>
  
Line 430: Line 459:
 ==== Cloning a generator ==== ==== Cloning a generator ====
  
-Generators can be cloned, thus leaving two independent ''Generator'' objects with the same stateThis behavior can for example be +Generators cannot be cloned.
-used to create a ''rewindable'' function, which would make the passed generator rewindable:+
  
-<code php> +Support for cloning was included in the initial versionbut removed in PHP 5.5 Beta 3 due to implementational difficulties, unclear semantics and no particularly convincing use cases.
-class RewindableGenerator implements Iterator { +
-    protected $original; +
-    protected $current; +
-     +
-    public function __construct(Generator $generator) { +
-        $this->original = $generator; +
-        $this->current = null; +
-    } +
-     +
-    public function rewind() { +
-        if ($this->current) { $this->current->close();+
-        $this->current = clone $this->original; +
-        $this->current->rewind(); +
-    } +
-     +
-    public function valid() { +
-        if (!$this->current) { $this->current = clone $this->original;+
-        return $this->current->valid(); +
-    } +
-     +
-    public function current() { +
-        if (!$this->current) { $this->current = clone $this->original;+
-        return $this->current->current(); +
-    } +
-     +
-    public function key() { +
-        if (!$this->current) { $this->current = clone $this->original;+
-        return $this->current->key(); +
-    } +
-     +
-    public function next() { +
-        if (!$this->current) { $this->current = clone $this->original;+
-        $this->current->next(); +
-    } +
-     +
-    public function send($value) { +
-        if (!$this->current) { $this->current = clone $this->original;+
-        return $this->current->send($value); +
-    } +
-     +
-    public function close() { +
-        $this->original->close(); +
-        if ($this->current) { +
-            $this->current->close(); +
-        } +
-    } +
-+
- +
-function rewindable(Generator $generator) { +
-    return new RewindableGenerator($generator); +
-+
-</code> +
- +
-It can be then used as follows: +
- +
-<code php> +
-function xrange($start, $end, $step = 1) { +
-    for ($i = $start; $i <= $end; $i += $step) { +
-        yield $i; +
-    } +
-+
- +
-$range = rewindable(xrange(0, 5)); +
-foreach ($range as $i) { +
-    echo $i, "\n"; +
-+
-foreach ($range as $i) { +
-    echo $i, "\n"; +
-+
-</code> +
- +
-This will correctly output the 0..5 range twice.+
  
 ==== Closing a generator ==== ==== Closing a generator ====
Line 538: Line 494:
 This is a list of generators-related error conditions: This is a list of generators-related error conditions:
  
-  * Manual construction of ''Generator'' class: ''E_RECOVERABLE_ERROR'' (analogous to ''Closure'' behavior) 
-  * Closing a generator while it is running: ''E_WARNING'' 
-  * Yielding a key that isn't an integer or a key: ''E_ERROR'' (this is just a placeholder until Etienne's arbitrary-keys patch lands) 
-  * Trying to iterate a non-ref generator by-ref: ''E_ERROR'' 
   * Using ''yield'' outside a function: ''E_COMPILE_ERROR''   * Using ''yield'' outside a function: ''E_COMPILE_ERROR''
   * Using ''return'' with a value inside a generator: ''E_COMPILE_ERROR''   * Using ''return'' with a value inside a generator: ''E_COMPILE_ERROR''
 +  * Manual construction of ''Generator'' class: ''E_RECOVERABLE_ERROR'' (analogous to ''Closure'' behavior)
 +  * Yielding a key that isn't an integer or a key: ''E_ERROR'' (this is just a placeholder until Etienne's arbitrary-keys patch lands)
 +  * Trying to iterate a non-ref generator by-ref: ''Exception''
 +  * Trying to traverse an already closed generator: ''Exception''
 +  * Trying to rewind a generator after the first yield: ''Exception''
   * Yielding a temp/const value by-ref: ''E_NOTICE'' (analogous to ''return'' behavior)   * Yielding a temp/const value by-ref: ''E_NOTICE'' (analogous to ''return'' behavior)
   * Yielding a string offset by-ref: ''E_ERROR'' (analogous to ''return'' behavior)   * Yielding a string offset by-ref: ''E_ERROR'' (analogous to ''return'' behavior)
Line 665: Line 622:
  
 The current implementation can be found in this branch: https://github.com/nikic/php-src/tree/addGeneratorsSupport. The current implementation can be found in this branch: https://github.com/nikic/php-src/tree/addGeneratorsSupport.
 +
 +I also created a PR so that the diff can be viewed more easily: https://github.com/php/php-src/pull/177
  
 ===== Vote ===== ===== Vote =====
  
-<doodle title="Should generators be merged into master?" auth="nikic" voteType="single" closed="false">+<doodle title="Should generators be merged into master?" auth="nikic" voteType="single" closed="true">
    * Yes    * Yes
    * No    * No
rfc/generators.1345910780.txt.gz · Last modified: 2017/09/22 13:28 (external edit)