rfc:generator-delegation

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
rfc:generator-delegation [2015/03/08 07:40]
rdlowrey v0.2.0
rfc:generator-delegation [2017/09/22 13:28] (current)
Line 2: Line 2:
   * Version: 0.2.0   * Version: 0.2.0
   * Date: 2015-03-01   * Date: 2015-03-01
-  * Authors: Daniel Lowrey <rdlowrey@php.net>+  * Author: Daniel Lowrey <rdlowrey@php.net>
   * Contributors: Bob Weinand <bwoebi@php.net>   * Contributors: Bob Weinand <bwoebi@php.net>
-  * Status: Under Discussion+  * Status: Implemented in 7.0
   * First Published at: http://wiki.php.net/rfc/generator-delegation   * First Published at: http://wiki.php.net/rfc/generator-delegation
  
 ====== Abstract ====== ====== Abstract ======
  
-This RFC proposes new ''yield from <expr>'' syntax allowing ''Generator'' functions to delegate operation to ''Traversable'' objects and arrays. The proposed syntax allows the factoring of ''yield'' statements into smaller conceptual units in the same way that discrete class methods simplify object-oriented code. The proposal is conceptually related to and requires functionality proposed by the forerunning [[rfc:generator-return-expressions|Generator Return Expressions RFC]].+This RFC proposes new ''yield from <expr>'' syntax allowing ''Generator'' functions to delegate operations to ''Traversable'' objects and arrays. The proposed syntax allows the factoring of ''yield'' statements into smaller conceptual units in the same way that discrete class methods simplify object-oriented code. The proposal is conceptually related to and requires functionality proposed by the forerunning [[rfc:generator-return-expressions|Generator Return Expressions]] RFC.
  
 ====== Proposal ====== ====== Proposal ======
Line 75: Line 75:
                 $send = false;                 $send = false;
             } else {             } else {
-                $throw $e;+                throw $e;
             }             }
         }         }
Line 101: Line 101:
 its eventual result. its eventual result.
  
-''yield from'' -- at its heart -- is nothing more than the application of standard factoring practices+Generator delegation -- at its heart -- is nothing more than the application of standard factoring practices
 to allow the decomposition of complex operations into smaller cohesive units. to allow the decomposition of complex operations into smaller cohesive units.
  
Line 114: Line 114:
     function myGeneratorFunction($foo) {     function myGeneratorFunction($foo) {
         // ... do some stuff with $foo ...         // ... do some stuff with $foo ...
-        $bar = yield factoredComputation1($foo);+        $bar = yield from factoredComputation1($foo);
         // ... do some stuff with $bar ...         // ... do some stuff with $bar ...
-        $baz = yield factoredComputation2($bar);+        $baz = yield from factoredComputation2($bar);
  
         return $baz;         return $baz;
Line 135: Line 135:
 ==== Use-Case: Generators as Lightweight Threads === ==== Use-Case: Generators as Lightweight Threads ===
  
-Perhaps the most powerful feature of Generator functions is their support for pausing execution +The defining feature of Generator functions is their support for suspending execution 
-with the capacity for later resumption. This capability gives applications a mechanism to+for later resumption. This capability gives applications a mechanism to
 implement asynchronous and concurrent architectures even in a traditionally single-threaded language implement asynchronous and concurrent architectures even in a traditionally single-threaded language
 like PHP. With simple userland task scheduling systems interleaved generators become lightweight like PHP. With simple userland task scheduling systems interleaved generators become lightweight
Line 142: Line 142:
  
 In the absence of generator return values, though, applications face an environment where "background" In the absence of generator return values, though, applications face an environment where "background"
-tasks can be offloaded without a standardized way to return the eventual results the result.+tasks can be offloaded without a standardized way to return the eventual result.
 This is one reason why this proposal depends on the acceptance of the Generator Return Expressions RFC. This is one reason why this proposal depends on the acceptance of the Generator Return Expressions RFC.
 The other reason return values are required stems from the previously discussed refactoring principle. The other reason return values are required stems from the previously discussed refactoring principle.
 Specifically: code using generators for threaded execution can benefit from subgenerators behaving Specifically: code using generators for threaded execution can benefit from subgenerators behaving
-like ordinary functions which accept values and return results.+like ordinary functions.
  
 Using the proposed syntax an ordinary function ''foo'' Using the proposed syntax an ordinary function ''foo''
Line 164: Line 164:
 simply by thinking of ''foo()'' as an ordinary function which can be suspended using a ''yield'' statement. simply by thinking of ''foo()'' as an ordinary function which can be suspended using a ''yield'' statement.
  
-**NB:** The actual implementation of single-threaded concurrency schedulers is outside the scope of+**NB:** The actual implementation of coroutine task schedulers is outside the scope of
 this document. This RFC focuses only on the language-level machinery needed to make such tools more this document. This RFC focuses only on the language-level machinery needed to make such tools more
 feasible in userland. It should be obvious that simply moving code into a generator function will feasible in userland. It should be obvious that simply moving code into a generator function will
-not instantly make it massively concurrent.+not somehow make it magically concurrent.
  
  
Line 333: Line 333:
 while($shared->valid()) { $shared->next(); } while($shared->valid()) { $shared->next(); }
 $delegator = delegator($shared); $delegator = delegator($shared);
-while($delegator->valid()) { $delegator->next(); } + 
-var_dump($delegator->getReturn()); /int(42)+foreach ($delegator as $value) { 
 +    var_dump($value); 
 +
 +var_dump($delegator->getReturn()); 
 + 
 +/
 +int(42) 
 +// This is our only output because no values are yielded 
 +// from the already-completed shared subgenerator 
 +*/
 </code> </code>
  
Line 366: Line 375:
 /* /*
 int(2); int(2);
 +int(3);
 int(4); int(4);
 int(42) int(42)
Line 373: Line 383:
 ==== Error States ==== ==== Error States ====
  
-There are two scenarios in which ''yield from'' usage can result in a fatal error or uncaught exception:+There are two scenarios in which ''yield from'' usage can result in an ''EngineException'':
  
-  * Using ''yield from <expr>'' where <expr> evaluates to a generator which previously terminated with an uncaught exception results in ''E_RECOVERABLE_ERROR'' (will convert to ''EngineException'' pending the outcome of the [[rfc:engine_exceptions_for_php7|Engine Exceptions RFC]]). +  * Using ''yield from <expr>'' where <expr> evaluates to a generator which previously terminated with an uncaught exception results in an ''EngineException''.
- +
-  * Using ''yield from <expr>'' where <expr> evaluates to something that is neither ''Traversable'' nor an array throws an ''Exception'' (will convert to ''EngineException'' pending the outcome of the [[rfc:engine_exceptions_for_php7|Engine Exceptions RFC]])+
  
 +  * Using ''yield from <expr>'' where <expr> evaluates to something that is neither ''Traversable'' nor an array throws an ''EngineException''.
      
 ===== Rejected Ideas ===== ===== Rejected Ideas =====
  
-The original version of this RFC proposed a ''yield *'' syntax. This was rejected in favor of +The original version of this RFC proposed a ''yield *'' syntax. The ''yield *'' syntax was rejected in favor of 
-''yield from'' on the basis that it would break backwards compatibility. Additionally, the ''yield *'' +''yield from'' on the basis that ''*'' would break backwards compatibility. Additionally, the ''yield *'' 
-form was considered less readable option that the current proposal.+form was considered less readable than the current proposal.
  
  
Line 406: Line 415:
 **Python** **Python**
  
-Python 3.3 generators support ''yield from'':+Python 3.3 generators support the ''yield from'' syntax:
  
 <code python> <code python>
->>> def g(x): +>>> def accumulate(): 
-...     yield from range(x, 0, -1) +...     tally = 
-...     yield from range(x)+...     while 1: 
 +...         next = yield 
 +...         if next is None: 
 +...             return tally 
 +...         tally += next
 ... ...
->>> list(g(5)) +>>> def gather_tallies(tallies): 
-[5, 4, 3, 2, 1, 0, 1, 2, 3, 4]+...     while 1: 
 +...         tally = yield from accumulate() 
 +...         tallies.append(tally
 +... 
 +>>> tallies = [
 +>>> acc = gather_tallies(tallies) 
 +>>> next(acc) # Ensure the accumulator is ready to accept values 
 +>>> for i in range(4): 
 +...     acc.send(i) 
 +... 
 +>>> acc.send(None) # Finish the first tally 
 +>>> for i in range(5): 
 +...     acc.send(i) 
 +... 
 +>>> acc.send(None) # Finish the second tally 
 +>>> tallies 
 +[610]
 </code> </code>
  
 **JavaScript** **JavaScript**
  
-Javascript ES6 generators support the ''yield from'' syntax:+Javascript ES6 generators support the ''yield*'' syntax:
  
 <code javascript> <code javascript>
-function* g1() { +function* g4() { 
-  yield 2+  yield* [1, 23]
-  yield 3; +  return "foo";
-  yield 4;+
 } }
  
-function* g2() { +var result; 
-  yield 1; + 
-  yield* g1()+function* g5() { 
-  yield 5;+  result = yield* g4();
 } }
  
-var iterator = g2();+var iterator = g5();
  
 console.log(iterator.next()); // { value: 1, done: false } console.log(iterator.next()); // { value: 1, done: false }
 console.log(iterator.next()); // { value: 2, done: false } console.log(iterator.next()); // { value: 2, done: false }
 console.log(iterator.next()); // { value: 3, done: false } console.log(iterator.next()); // { value: 3, done: false }
-console.log(iterator.next()); // { value: 4, done: false +console.log(iterator.next()); // { value: undefined, done: true } 
-console.log(iterator.next()); // { value: 5, done: false +                              // g4() returned { value: "foo", done: true at this point 
-console.log(iterator.next()); // { value: undefined, done: true }+ 
 +console.log(result);          // "foo"
 </code> </code>
  
Line 457: Line 486:
  
  
-===== Proposed Voting Choices ===== +===== Vote =====
- +
-Two voting choices are proposed†:+
  
-  * **YES**, implement the ''yield from'' syntax allowing generator delegation in PHP 7*+A 2/3 "Yes" vote is required to implement this proposal. Voting will continue through March 29, 2015.
  
-  * **NO**, do not modify existing Generator behavior+<doodle title="Allow Generator delegation in PHP7" auth="rdlowrey" voteType="single" closed="true"> 
 +   * Yes 
 +   No 
 +</doodle>
  
-A 2/3 "Yes" vote is required to implement this proposal.+.
  
-†: The success of this vote depends on the success of the accompanying [[rfc:generator-return-expressions|Generator Return Expressions]] RFC. +The success of this vote depends on the success of the accompanying [[rfc:generator-return-expressions|Generator Return Expressions]] RFC. Should Generator Return Expressions be rejected the voting outcome of this RFC will be rendered moot.
-Should Generator Return Expressions be rejected the voting outcome of this RFC will be rendered moot.+
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
  
-The current patch is approaching "final" status and can be found here:+The current patch is considered "final" and can be found here:
  
 https://github.com/bwoebi/php-src/commits/coroutineDelegation https://github.com/bwoebi/php-src/commits/coroutineDelegation
rfc/generator-delegation.1425800410.txt.gz · Last modified: 2017/09/22 13:28 (external edit)