# [RFC] Generators

- Thread: https://externals.io/message/62116
- Original archive: https://news-web.php.net/php.internals/62116
- Started by Nikita Popov on 2012-08-11T13:42:59+00:00
- Messages: 38
- Page 1 of 2: messages 1 to 25 (25 per page, add `?page=N` to the URL)
- Next page: https://externals.io/message/62116.md?page=2
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #62116 — Nikita Popov — 2012-08-11T13:42:59+00:00

URL: https://externals.io/message/62116#62116

Hi internals!

I think there already was a lot of discussion on the generators, so
it's time to move to the next step. I'd like to vote on the feature in
two weeks, so this the "announce[ment] on internals@, by the author,
with the intention of voting on it".

    https://wiki.php.net/rfc/generators

If you have any further feedback, now would be a good time to raise
it. If there is something you previously posted, but which I didn't
yet address, please let me know. There were around 150 mails and I
sure missed some of them.

Thanks,
Nikita

---

## #62118 — Brian Moon — 2012-08-12T20:08:23+00:00

URL: https://externals.io/message/62116#62118 · In reply to: #62116

Hi Nikita,

I admit, I have ignored these threads as there was no RFC. So, some of 
this may have been covered.

Do you have a good example usage other than a file? I don't find 
fopen/fgets/fclose all that complicated. What are the other valid use 
cases for such a thing?

Also, not allowing rewinding is unintuitive for something that is an 
iterator in PHP. If I can foreach() it and I can call `next()` on it, I 
expect to be able to `reset()` it as well. IMO, you would need to issue a 
FATAL PHP error if that simply is not allowed. Or you have to have a 
second syntax for what to do in that case. At that point, you are 
implementing Iterator.

While I am glad that PHP has borrowed syntax from many languages, I find 
the yield keyword to be very WTF when I first saw it. It is also poorly 
explained in your RFC. You use terms like "sending and receiving". That 
does not say "returns from function execution" to me. I had to basically 
figure it out from the code examples.

Lastly, because you cite needing this for sending data to PHPUnit, I 
think this is a user land problem and not a core problem. In about 5 
minutes I implemented a reusable Generator object that does exactly what 
you need. http://pastebin.com/V336rEpR At least, it does what your 
examples show you need. Again, file IO is very easy and perhaps that 
example does not explain your true need very well.

Brian
brianlmoon@php.net
http://brian.moonspot.net/


> Hi internals!
>
> I think there already was a lot of discussion on the generators, so
> it's time to move to the next step. I'd like to vote on the feature in
> two weeks, so this the "announce[ment] on internals@, by the author,
> with the intention of voting on it".
>
>      https://wiki.php.net/rfc/generators
>
> If you have any further feedback, now would be a good time to raise
> it. If there is something you previously posted, but which I didn't
> yet address, please let me know. There were around 150 mails and I
> sure missed some of them.
>
> Thanks,
> Nikita

---

## #62119 — Levi Morrison — 2012-08-12T22:33:23+00:00

URL: https://externals.io/message/62116#62119 · In reply to: #62118

> Do you have a good example usage other than a file? I don't find fopen/fgets/fclose all that complicated. What are the other valid use cases for such a thing?

One fabulous use case is creating an iterator for a Binary Search
Tree.  A post order done without generators looks like:
https://github.com/morrisonlevi/PHP-Datastructures/blob/master/src/Spl/PostOrderIterator.php.
 An iterator using a generator looks something like:

    public function getIterator() {
        if ($this->left) yield* $this->left;
        yield $this->value;
        if ($this->right) yield* $this->right;
    }

This is 5 lines.  The fully commented version of the post-order
iterator previously mentioned is 106 lines of code and is considerably
harder to understand.

---

## #62120 — Yahav Gindi Bar — 2012-08-12T22:55:23+00:00

URL: https://externals.io/message/62116#62120 · In reply to: #62119

> 
> > […]
> 
> One fabulous use case is creating an iterator for a Binary Search
> Tree.  A post order done without generators looks like:
> https://github.com/morrisonlevi/PHP-Datastructures/blob/master/src/Spl/PostOrderIterator.php.
> An iterator using a generator looks something like:
> 
>    public function getIterator() {
>        if ($this->left) yield* $this->left;
>        yield $this->value;
>        if ($this->right) yield* $this->right;
>    }
> 
> This is 5 lines.  The fully commented version of the post-order
> iterator previously mentioned is 106 lines of code and is considerably
> harder to understand.
> 
> -- 
> 
> 
> 
Each Iterator implementation in PHP is longer and require a class. The yield keyword can reduce the code and make things simpler. If my vote count, though I can't believe it is, I'd vote for it - clean syntax, make things simpler to develop after you understand the concept (for those who against adding because of the learning issue and beginners - we'll have beginners anytime that'll mess up with the concept, so I don't think that it should restrict us. Put that aside, we should make the documentation clear and easy as possible in order to help beginners understand the concept).

---

## #62121 — Brian Moon — 2012-08-12T23:56:49+00:00

URL: https://externals.io/message/62116#62121 · In reply to: #62119

> 
> > […]
>
> One fabulous use case is creating an iterator for a Binary Search
> Tree.  A post order done without generators looks like:
> https://github.com/morrisonlevi/PHP-Datastructures/blob/master/src/Spl/PostOrderIterator.php.
>   An iterator using a generator looks something like:
>
>      public function getIterator() {
>          if ($this->left) yield* $this->left;
>          yield $this->value;
>          if ($this->right) yield* $this->right;
>      }
>
> This is 5 lines.  The fully commented version of the post-order
> iterator previously mentioned is 106 lines of code and is considerably
> harder to understand.

Well, it's 52 lines, not 106. The 5 lines above are not commented nor 
are they spaced at all like the ones in the class. In the above example, 
what sets $this->right? or $this->left? There has to be more calling 
code around this. I don't consider this a very good example.

Brian.
brianlmoon@php.net

---

## #62122 — Levi Morrison — 2012-08-13T01:14:46+00:00

URL: https://externals.io/message/62116#62122 · In reply to: #62121

> 
> > […]
>
>
> Well, it's 52 lines, not 106. The 5 lines above are not commented nor are
> they spaced at all like the ones in the class.

No, Brian, it is 106 lines of actual code I would find in the wild.
And sure, I'd have spaced the generator code more like:
     public function getIterator() {
          if ($this->left) {
              yield* $this->left;
          }

          if ($this->right) {
              yield* $this->right;
          }

          yield $this->value;
      }

However, that's 11 lines of code compared to 106.

> In the above example, what
> sets $this->right? or $this->left?

Iterators don't set $this->right and $this->left; they only traverse the tree.

> I don't consider this a very good example.
That's fine.  You are entitled to your opinion. However, you didn't
have to figure out the 106 lines of code that is the Post-Order
iterator.  That was a NIGHTMARE and I'm still not 100% confident that
it works as it is supposed to.  The generator, on the other hand, is
simple and follows the definition very closely.  MUCH better.

---

## #62123 — Levi Morrison — 2012-08-13T01:20:43+00:00

URL: https://externals.io/message/62116#62123 · In reply to: #62122

I guess to be completely honest, there would be a docblock above the
generator code as well.  So 14 lines.

---

## #62124 — Brian Moon — 2012-08-13T02:25:47+00:00

URL: https://externals.io/message/62116#62124 · In reply to: #62122

> > […]
> That's fine.  You are entitled to your opinion. However, you didn't
> have to figure out the 106 lines of code that is the Post-Order
> iterator.  That was a NIGHTMARE and I'm still not 100% confident that
> it works as it is supposed to.  The generator, on the other hand, is
> simple and follows the definition very closely.  MUCH better.

Could you show the whole class that this public function belongs to? I 
don't see how this function fits in and compares at all to the class you 
linked.

Brian.

---

## #62125 — Levi Morrison — 2012-08-13T02:39:49+00:00

URL: https://externals.io/message/62116#62125 · In reply to: #62124

> > […]
>
>
> Could you show the whole class that this public function belongs to? I don't
> see how this function fits in and compares at all to the class you linked.
>
> Brian.

It renders the iterator class completely obsolete; it could be part of
the Binary Search Tree class.

If you still wanted a separate class (separation of concerns, perhaps)
it would look something like this (not sure on exact yield syntax):

class PostOrderIterator implements IteratorAggregate {

    /**
     * @var BinaryTree
     */
    protected $root;

    public function __construct(BinaryTree $root) {
        $this->root = $root;
    }

    /**
     * @link http://php.net/manual/en/iteratoraggregate.getiterator.php
     * @return Traversable
     */
    public function getIterator() {
        yield $this->traverse($this->root);
    }

    private function traverse(BinaryTree $node) {
        if ($node->left) {
            yield* $node->left;
        }

        if ($node->right) {
            yield* $node->right;
        }

        yield $node->value;
    }
}

All this does is take the node that acts as root in it's constructor
and then calls the generator with root as the starting point. That's
34 lines of code including comments and whitespace.

---

## #62126 — Anthony Ferrara — 2012-08-13T02:49:16+00:00

URL: https://externals.io/message/62116#62126 · In reply to: #62118

Brian,



> Hi Nikita,
>
> I admit, I have ignored these threads as there was no RFC. So, some of
> this may have been covered.
>

There was an RFC in those posts... It was just being iterated over.


> Do you have a good example usage other than a file? I don't find
> fopen/fgets/fclose all that complicated. What are the other valid use cases
> for such a thing?
>

Here's a quick set of examples:
http://blog.ircmaxell.com/2012/07/what-generators-can-do-for-you.html


> Also, not allowing rewinding is unintuitive for something that is an
> iterator in PHP. If I can foreach() it and I can call `next()` on it, I
> expect to be able to `reset()` it as well. IMO, you would need to issue a
> FATAL PHP error if that simply is not allowed. Or you have to have a second
> syntax for what to do in that case. At that point, you are implementing
> Iterator.
>

I partially agree. rewinding the generator might be possible, but it's hard
to tell in those cases. It's hard to tell if resetting it is even possible
from the code level. So I'm pretty much OK with living with that
restriction for the time being...


> While I am glad that PHP has borrowed syntax from many languages, I find
> the yield keyword to be very WTF when I first saw it. It is also poorly
> explained in your RFC. You use terms like "sending and receiving". That
> does not say "returns from function execution" to me. I had to basically
> figure it out from the code examples.
>

It's absolutely something that takes getting used to. But it's also quite
intuitive once you think about it. You're not "returning" back to the
parent scope (exiting your scope), you're "yielding" a value to the parent
scope, expecting to continue on later (think of it as a stop light where
you let the other code run for a while until you go to the next one).


> Lastly, because you cite needing this for sending data to PHPUnit, I think
> this is a user land problem and not a core problem. In about 5 minutes I
> implemented a reusable Generator object that does exactly what you need.
> http://pastebin.com/V336rEpR At least, it does what your examples show
> you need. Again, file IO is very easy and perhaps that example does not
> explain your true need very well.
>

Well, there's one thing that should be made clear. There's nothing (and I
mean that) that generators provide that you can't do already. You can build
iterators that do exactly the same thing. The trick that generators provide
you is a really short, and really easy way of implementing iterators. You
don't need to worry about maintaining state or anything like that. All you
need to do is code it how you would normally, and replace your executing
code with a yield. No more needing to split out and maintain state in weird
ways, or using literally hundreds of lines of code (because of the
boilerplate involved) where a dozen would do...

Anthony

---

## #62129 — Nikita Popov — 2012-08-13T16:09:32+00:00

URL: https://externals.io/message/62116#62129 · In reply to: #62118

> Also, not allowing rewinding is unintuitive for something that is an
> iterator in PHP. If I can foreach() it and I can call `next()` on it, I expect
> to be able to `reset()` it as well. IMO, you would need to issue a FATAL PHP
> error if that simply is not allowed. Or you have to have a second syntax for
> what to do in that case. At that point, you are implementing Iterator.

Currently I'm planning to implement the following behavior for `rewind()`:

 * If before first yield: Resume to first yield (this priming behavior
is common to all the Iterator methods)
 * At first yield: No-op
 * After first yield: Recoverable fatal error

So this would allow you to call ->rewind() after creating the
generator, but will throw an error if you try to do so later.

Another thing that I'd like to do is drop the ->close() method. It
doesn't really make sense to explicitly close generators.

Any objects to those two things?

Nikita

---

## #62141 — Alex Aulbach — 2012-08-14T12:02:17+00:00

URL: https://externals.io/message/62116#62141 · In reply to: #62126

2012/8/13 Anthony Ferrara <ircmaxell@gmail.com>:
> It's absolutely something that takes getting used to. But it's also quite
> intuitive once you think about it. You're not "returning" back to the
> parent scope (exiting your scope), you're "yielding" a value to the parent
> scope, expecting to continue on later (think of it as a stop light where
> you let the other code run for a while until you go to the next one).

I think for people, which are - like me - more used with databases,
the cursor-concept is a good comparison.

Perhaps this could help a little bit to explain it...

-- 
Alex Aulbach

---

## #62142 — Alex Aulbach — 2012-08-14T12:37:48+00:00

URL: https://externals.io/message/62116#62142 · In reply to: #62129

2012/8/13 Nikita Popov <nikita.ppv@gmail.com>:
>  * After first yield: Recoverable fatal error
>
> So this would allow you to call ->rewind() after creating the
> generator, but will throw an error if you try to do so later.

Hm. I think this looks like a workaround over conceptual problems.
The (in my eyes) "right" behavior would be to call a new instance of
the generator if you rewind and destroy the current (= replace current
generator with a new).

I would prefer to say "this is not an real iterator"; if you want to
make it an real iterator, then implement it yourself.

Otherwise it's like "Ok, we have here a new type of function which
looks enough like a function to be a function, which implements an
iterator which looks enough like an iterator to be an iterator.". :)


I also recommend to say, that generators aren't a good idea if used on
resources, which can't be shared. For example: the file-read-example
doesn't lock. To implement locking new functions or a new class is
needed - hiding complexity.

Generators are (in my opinion) really nice for things which are
already in the memory. Not more or less.

-- 
Alex Aulbach

---

## #62145 — hakre — 2012-08-14T15:39:43+00:00

URL: https://externals.io/message/62116#62145 · In reply to: #62129

If I may chime in, not all iterators support rewind, at least not as outlined.

There exists also the NoRewindIterator which you can just use to decorate an iterator out of a generator to not make it do a fatal.

However as far as a generator is concerned, it probably should just behave like NoRewindIterator already so not give any errors on rewind. Because you can not rewind what you can not rewind. No need to error, no need to do something. The function is just there because of the interface, but it must not do anything.

If different behavior is needed it could be decorated, but right now I can not imagine why rewind would be called (apart from using in foreach() for the first time which implies a rewind and that one is already like I suggest.)


My 2 cents.

-- hakre



----- Ursprüngliche Message -----
> Von: Nikita Popov <nikita.ppv@gmail.com>
> An: Brian Moon <brian@moonspot.net>
> CC: PHP internals <internals@lists.php.net>
> Gesendet: 18:09 Montag, 13.August 2012
> Betreff: Re: [PHP-DEV] [RFC] Generators
> 
> 
> > […]
> expect
> > […]
> for
> > […]
> 
> Currently I'm planning to implement the following behavior for `rewind()`:
> 
> * If before first yield: Resume to first yield (this priming behavior
> is common to all the Iterator methods)
> * At first yield: No-op
> * After first yield: Recoverable fatal error
> 
> So this would allow you to call ->rewind() after creating the
> generator, but will throw an error if you try to do so later.
> 
> Another thing that I'd like to do is drop the ->close() method. It
> doesn't really make sense to explicitly close generators.
> 
> Any objects to those two things?
> 
> Nikita

---

## #62147 — Aaron Holmes — 2012-08-14T16:42:35+00:00

URL: https://externals.io/message/62116#62147 · In reply to: #62129

> 
> > […]
> Currently I'm planning to implement the following behavior for `rewind()`:
>
>   * If before first yield: Resume to first yield (this priming behavior
> is common to all the Iterator methods)
>   * At first yield: No-op
>   * After first yield: Recoverable fatal error
>
> So this would allow you to call ->rewind() after creating the
> generator, but will throw an error if you try to do so later.
>
My perspective is that generators are intended to generate and yield a 
result, not so much to iterate over a known set of results. Thus, the 
results not really being known, there is nothing to rewind to. Rewinding 
would also presumably require more state control, or keeping track of 
previous results, and defeat the memory advantages of generators. 
Perhaps this is erroneous?

That said, `rewind()` should behave consistently. I don't feel it makes 
sense to have `rewind()` succeed at one point, and fail at another. It 
would only cause confusion when not familiar with the behavior. Either 
allow it, or don't. Not both.

Thanks,
Aaron Holmes

---

## #62151 — Stas Malyshev — 2012-08-14T17:36:50+00:00

URL: https://externals.io/message/62116#62151 · In reply to: #62147

Hi!

> That said, `rewind()` should behave consistently. I don't feel it makes 
> sense to have `rewind()` succeed at one point, and fail at another. It 
> would only cause confusion when not familiar with the behavior. Either 
> allow it, or don't. Not both.

It does, since foreach uses rewind. So first rewind should succeed if
you want iterators be usable in foreach. OTOH, on something like DB
result set, next rewind does not make any sense, if you have
non-seekable cursor, since the results consumed are gone and there's no
way to get them back (you could rerun the query, but it might have side
effects and nobody guarantees you'd get the same result anyway). So it
makes sense for the generator to succeed on first rewind but fail on
next ones. Note that generators by nature are stateful objects, so it is
not unexpected that they would produce different result in different
states.

-- 
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227

---

## #62152 — Aaron Holmes — 2012-08-14T17:59:03+00:00

URL: https://externals.io/message/62116#62152 · In reply to: #62151

> Hi!
>
> > […]
> It does, since foreach uses rewind. So first rewind should succeed if
> you want iterators be usable in foreach. OTOH, on something like DB
> result set, next rewind does not make any sense, if you have
> non-seekable cursor, since the results consumed are gone and there's no
> way to get them back (you could rerun the query, but it might have side
> effects and nobody guarantees you'd get the same result anyway). So it
> makes sense for the generator to succeed on first rewind but fail on
> next ones. Note that generators by nature are stateful objects, so it is
> not unexpected that they would produce different result in different
> states.
>
Thanks for clarifying. It makes sense now, considering foreach's 
behavior and the generators statefulness allowing what otherwise seems 
inconsistent.
However, might it make sense to no-op instead of erroring? If generators 
allow `rewind()`, it would be unexpected to receive an error for calling it.

---

## #62236 — Nikita Popov — 2012-08-18T10:03:57+00:00

URL: https://externals.io/message/62116#62236 · In reply to: #62152

> Thanks for clarifying. It makes sense now, considering foreach's behavior
> and the generators statefulness allowing what otherwise seems inconsistent.
> However, might it make sense to no-op instead of erroring? If generators
> allow `rewind()`, it would be unexpected to receive an error for calling it.

A no-op is the current behavior. The issue with that is that you could
accidentally reuse an already depleted (or partially depleted)
generator. I.e. you run it through one loop and then through another
one. With a no-op rewind it would be hard to figure out what the issue
is. The error makes clear that you tried to iterate an already
iterated generator.

Furthermore, if you indeed *want* to partially traverse a generator in
several loops, you can simply wrap it in a NoRewindIterator. This has
the additional benefit of making it more clear what you want to do.

Nikita

---

## #62237 — Derick Rethans — 2012-08-18T12:21:37+00:00

URL: https://externals.io/message/62116#62237 · In reply to: #62116

> Hi internals!
> 
> I think there already was a lot of discussion on the generators, so
> it's time to move to the next step. I'd like to vote on the feature in
> two weeks, so this the "announce[ment] on internals@, by the author,
> with the intention of voting on it".
> 
>     https://wiki.php.net/rfc/generators
> 
> If you have any further feedback, now would be a good time to raise
> it. If there is something you previously posted, but which I didn't
> yet address, please let me know. There were around 150 mails and I
> sure missed some of them.

I've some comments how that I've read the RFC:

> Recognition of generator functions
> 
> 1. Any function which contains a yield statement is automatically a 
> generator function.
> 
> 2. The initial implementation required that generator functions are 
> marked with an asterix modifier (function*). This method has the 
> advantage that generators are more explicit and also allows for 
> yield-less coroutines.
> 
> The automatic detection was chosen over the asterix modifier for the 
> following reasons:

I am against this. This is even more magic in PHP. Is it really that 
difficult to have to mark the function with a different keyword, such as 
"generator":

> generator function getLinesFromFile($fileName) {
>     if (!$fileHandle = fopen($fileName, 'r')) {
>        return;
>    }

>     There is an existing generator implementation in HipHop PHP, which 
> uses automatic-detection. Using the asterix modifier would break 
> compatibility.

This should not be a concern, sure, it's annoying for the hiphop 
developers but they chose to copy and then *chance* the PHP language for 
their own effect.

> yield: Yields the value null with an auto-incrementing integer key.

What is the usecase for this?

cheers,
Derick

-- 
http://derickrethans.nl | http://xdebug.org
Like Xdebug? Consider a donation: http://xdebug.org/donate.php
twitter: @derickr and @xdebug

---

## #62238 — Nikita Popov — 2012-08-18T13:19:41+00:00

URL: https://externals.io/message/62116#62238 · In reply to: #62237

> I've some comments how that I've read the RFC:
>
> > […]
>
> I am against this. This is even more magic in PHP. Is it really that
> difficult to have to mark the function with a different keyword, such as
> "generator":

There was already a rather long discussion on this topic. Most of it
is noise, but I filtered out a few mails from Sara and Rasmus as they
probably are of most interest:

http://markmail.org/message/xzhdhbjozb4yrhh3
http://markmail.org/message/ryhygtimpd7q2nok
http://markmail.org/message/32fklwqykpk56iph
http://markmail.org/message/w2kbh7psplnmctcr

> > […]
>
> What is the usecase for this?

Use case is `$data = yield;`. In that case you don't care about what
you yield (so just the default value and key are yielded), you are
only interested in what you get sent back.

Nikita

---

## #62242 — lester@lsces.co.uk — 2012-08-18T14:12:32+00:00

URL: https://externals.io/message/62116#62242 · In reply to: #62237

> 
>
> > […]
>
> I've some comments how that I've read the RFC:
>
> > […]
>
> I am against this. This is even more magic in PHP. Is it really that
> difficult to have to mark the function with a different keyword, such as
> "generator":

Since this is yet another area where 'one does not have to use it if one
does not want to' ... FLAGGING to the other users that a function is a
'special one' rather than just a normal function with a generator function
seems to me just a necessity? HAVING to work through a functions code
simply to see if it contains a 'yeild' so as to understand that it's not
going to give a normal return seems insane? Alright the function can be
properly documented with a docblock, but if it isn't ...

> > […]
>
> > […]
>
> This should not be a concern, sure, it's annoying for the hiphop
> developers but they chose to copy and then *chance* the PHP language for
> their own effect.
>
> > […]
> What is the usecase for this?
I can see some interesting porting problems with this ... one of the stock
changes when moving from a MySQL only setup to support other databases is
to remove the auto-increment magic, and replace it with proper sequence
code prior to inserting. I can see translating a 'MySQL' geared generator
into a more general one as being impossible if some tricks like this are
used.

(Out in an exhibition hall this weekend so not able to follow properly)

---

## #62243 — Rasmus Lerdorf — 2012-08-18T14:28:20+00:00

URL: https://externals.io/message/62116#62243 · In reply to: #62242

> Since this is yet another area where 'one does not have to use it if one
> does not want to' ... FLAGGING to the other users that a function is a
> 'special one' rather than just a normal function with a generator function
> seems to me just a necessity? HAVING to work through a functions code
> simply to see if it contains a 'yeild' so as to understand that it's not
> going to give a normal return seems insane? Alright the function can be
> properly documented with a docblock, but if it isn't ...

I don't see how that is any different from having to look through a
function to see if it is a void function or if it returns something, and
if so, what it returns. Or checking to see if it outputs something.

PHP is very dynamic. A function can be both void and non-void at the
same time. It can return mixed types and do pretty much anything you can
imagine. I don't really see what the generator keyword achieves. What
happens if I have a function that I mark as a generator but then I don't
put a yield in it? Is that a compile-level error? That might work, but
then what if I put a yield in it but the logic of the function never
gets to it:

  if(cond) return [1,2,3];
  else if(other_cond) yield $val;
  else return;

There is no way to know at compile-time whether the yield will ever be
reached.

To me it seems odd to have a generator keyword that cannot be enforced
and doesn't mean anything other than stating a developer's intent.
Whether the developer actually carried through and implemented the
intended generator can only be verified by reading the code. This smells
much more like a source comment to me.

If we were going to go with a generator keyword, then I think the
feature needs to be completely re-thought. I would suggest dropping the
yield keyword entirely and having return be equivalent to a yield if the
function is marked as a generator. Then the keyword actually does something.

-Rasmus

---

## #62244 — Nikita Popov — 2012-08-18T15:56:56+00:00

URL: https://externals.io/message/62116#62244 · In reply to: #62242

> Since this is yet another area where 'one does not have to use it if one
> does not want to' ... FLAGGING to the other users that a function is a
> 'special one' rather than just a normal function with a generator function
> seems to me just a necessity? HAVING to work through a functions code
> simply to see if it contains a 'yeild' so as to understand that it's not
> going to give a normal return seems insane? Alright the function can be
> properly documented with a docblock, but if it isn't ...

I don't understand this argument. Generator functions are transparent
to the user. You use a generator function just like you would use a
function that returns an array. From a user point of view it does not
matter whether getLinesFromFile() is just a function returning an
array, or whether it is a generator (or maybe even returns a
hand-implemented iterator). It's just all the same. The fact that the
function uses `yield` internally is just an implementation detail, not
something that has to be part of the public API. Actually you can swap
between an array and generator implementation just by replacing one
line in the function body (yield <=> $array[])...

> > […]
> I can see some interesting porting problems with this ... one of the stock
> changes when moving from a MySQL only setup to support other databases is
> to remove the auto-increment magic, and replace it with proper sequence
> code prior to inserting. I can see translating a 'MySQL' geared generator
> into a more general one as being impossible if some tricks like this are
> used.

Generators are supposed to act very similar to array-returning
functions, so they also have similar key semantics. If you do
$result[] = $foo; that will behave the same as doing yield $foo;. Both
will use an auto-incrementing key.

Nikita

---

## #62247 — Lester Caine — 2012-08-18T18:03:23+00:00

URL: https://externals.io/message/62116#62247 · In reply to: #62244

Nikita Popov wrote:

> I don't understand this argument. Generator functions are transparent
> to the user. You use a generator function just like you would use a
> function that returns an array. From a user point of view it does not
> matter whether getLinesFromFile() is just a function returning an
> array, or whether it is a generator (or maybe even returns a
> hand-implemented iterator). It's just all the same. The fact that the
> function uses `yield` internally is just an implementation detail, not
> something that has to be part of the public API. Actually you can swap
> between an array and generator implementation just by replacing one
> line in the function body (yield <=> $array[])...

Then I am now totally confused ...

I was under the impression that the IDEA was that EACH call to a generator would 
return the next value? So you do not get an array built. Just as one would with 
SUSPEND in an SQL query process. The only value available is the current one, 
and one processes that. So that the user of a generator needs to handle the 
results sequentially rather than simply accessing the array of results. There 
does not need to be an array of results, only the processed output of each cycle 
of the generator?

This of cause may still be confused thinking, since I would never be working in 
the way that this stuff is supposed to simplify. I WOULD be handling each 
element as I read it and building the details required from each cycle of the 
process. getLinesFromFile() would only ever return an array if that is what was 
needed to build the resulting page. It would normally be processed into a 
database, and later the required information selected from that. But that is a 
specialist iterator process just built with simple PHP.

-- 
Lester Caine - G8HFL
-----------------------------
Contact - http://lsces.co.uk/wiki/?page=contact
L.S.Caine Electronic Services - http://lsces.co.uk
EnquirySolve - http://enquirysolve.com/
Model Engineers Digital Workshop - http://medw.co.uk
Rainbow Digital Media - http://rainbowdigitalmedia.co.uk

---

## #62268 — Stas Malyshev — 2012-08-19T23:07:55+00:00

URL: https://externals.io/message/62116#62268 · In reply to: #62237

Hi!

> I am against this. This is even more magic in PHP. Is it really that 
> difficult to have to mark the function with a different keyword, such as 
> "generator":

You have a point here, but "public static final generator function
foo()" sounds a bit long-winded to me... Also, we'd have then to decide
which function can be marked generator and which can't (e.g., interface
probably can't, abstract probably can't, anonymous probably can, etc.)
which adds more complexity.

Also, I think that people that complain about having to scan through
huge functions to see if they're generators or not, forget one thing:
documentation. Yes, there is a way to make the purpose of the function
understandable to a human without having him to do computer's work.
That's documentation. Undocumented code is broken code. Broken code is
not a good example when we're talking about right design.

> This should not be a concern, sure, it's annoying for the hiphop 
> developers but they chose to copy and then *chance* the PHP language for 
> their own effect.

Here I tend to agree with you - we should base on what's right for PHP,
not what HipHop or any other implementation is doing. If we can make
their lives easier - fine, but we don't have to be bound by their
decisions.
-- 
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227

---

Next page: https://externals.io/message/62116.md?page=2
