Welcome! Log In Create A New Profile

Advanced

[PHP-DEV] Generators in PHP

Posted by Nikita Popov 
Andrew Faulds
Re: [PHP-DEV] Generators in PHP
August 08, 2012 10:30PM
On 08/08/12 21:14, Nikita Popov wrote:
> On Fri, Jul 27, 2012 at 8:09 PM, Nikita Popov <[email protected]> wrote:
>>> 5. Are multiple yields allowed? I.e. the rfc mentions something like
>>> yield yield $a - what that would mean? I'd allow yield only be applied
>>> to variable expression (lval) because double yield doesn't make sense to
>>> me, but maybe I miss something.
>> yield yield $a would basically first yield $a and then receive some
>> value (via send) and yield that value again. Nothing you'd normally do
>> ;) It was mentioned only as a syntax ambiguity consideration.
>>
>> Actually I added some additional parenthesis requirements for yield.
>> For example you'd have to write the above as `yield (yield $a)` now
>> (which should be slightly more clear). I haven't yet reflected this
>> change in the RFC, but I'll add a section on it later.
>>
>>> 6. “Sending values” section seems to be missing. Especially useful would
>>> be to cover what happens with keys there and what is the syntax there -
>>> is it just "$a = yield;"? Or does it mean when yield is used in
>>> expression it becomes incoming yield? And, last but not least - do we
>>> need sending into generators at all?
>> Yeah, I haven't written that section yet. But it is fairly simple: If
>> you go $generator->send($foo) then $foo will be the result of the
>> current `yield` expression. And yes, this also works with keys and
>> values. All of the following are valid:
>>
>> $data = yield;
>> $data = (yield $value);
>> $data = (yield $key => $value);
>>
>> The first case is the most common though. I.e. you usually use it
>> either as a generator or a reverse generator, not both. But doing both
>> is also common for cooperative multitasking etc.
>>
>> Regarding the last question: I think the feature is worth adding. It
>> is a very powerful concept that is hard to implement otherwise. Useful
>> in particular for things like parsing and multitasking.
>>
>>> 6. What happens if you send into a by-ref generator? Is the data sent
>>> by-ref then? What if it's an expression that can't be send by-ref?
>> No, sending is always by-value. By-ref only affects the yielding part.
> I now added the mentioned sections:
>
> https://wiki.php.net/rfc/generators#yield_keyword
> https://wiki.php.net/rfc/generators#sending_values
>
> I also added a list of error conditions:
>
> https://wiki.php.net/rfc/generators#error_conditions
>
> Sorry for the delay,
> Nikita
>
Hi Nikita,

I notice you require brackets round yield $k => $v and yield $v. Is this
the formal syntax, i.e. '(' T_YIELD var => var ')'? If so it makes sense
in a way, but it feels a little hackish. Does yield $v cause some sort
of parsing issue that putting it in brackets solves? I realise that it's
less ambiguous, but I don't like the idea of it. PHP's syntax has enough
special cases already IMO.

Regards,

--
Andrew Faulds
http://ajf.me/


--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Nikita Popov
Re: [PHP-DEV] Generators in PHP
August 08, 2012 10:50PM
On Wed, Aug 8, 2012 at 10:27 PM, Andrew Faulds <[email protected]> wrote:
> Hi Nikita,
>
> I notice you require brackets round yield $k => $v and yield $v. Is this the
> formal syntax, i.e. '(' T_YIELD var => var ')'? If so it makes sense in a
> way, but it feels a little hackish. Does yield $v cause some sort of parsing
> issue that putting it in brackets solves? I realise that it's less
> ambiguous, but I don't like the idea of it. PHP's syntax has enough special
> cases already IMO.

Without parenthesis their behavior in array definitions and nested
yields is ambigous:

array(yield $key => $value)
// can be either
array((yield $key) => $value)
// or
array((yield $key => $value))

yield yield $key => $value;
// can be either
yield (yield $key) => $value;
// or
yield (yield $key => $value);

Apart from that particular case there is the general operator
precedence inclarity, e.g.

yield $foo . $bar;
// could be
(yield $foo) . $bar;
// or
yield ($foo . $bar);

This obviously is not a problem per-se, but with yield-style unary
operators the precedence rules are often hard to figure out. E.g. most
people would probably think that

if (include('foo.php') == true) { ... }

would be check the return value of include, but it's actually not.
Rather it includes the file ('foo.php') == true, which is ''.

Another issue was a purely implementational: In order to support
by-ref yielding I have to distinguish between variable and
non-variable expressions, which breaks the usual precedence rules. I
was not able to fix the shift/reduce conflicts that are created by
this without requiring the parenthesis.

Also I'd like to mention that Python also has the paren-requirement
for yield-expressions. It even requires parens for a simple yield,
i.e. you'd have to write "data = (yield)". This is not necessary in
PHP, because PHP has semicolons.

Nikita

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Stas Malyshev
Re: [PHP-DEV] Generators in PHP
August 08, 2012 10:50PM
Hi!


> https://wiki.php.net/rfc/generators#yield_keyword
> https://wiki.php.net/rfc/generators#sending_values

I'm not sure $data = (yield $value) makes a lot of sense. Why we have
two variables here? Why it is only operator in the language that
requires parentheses around? All these complex parentheses rules seem
unnecessarily complicated. I'd rather do it in a more simple way: yield
in an expression means incoming yield. I.e.:
$data = yield;
foo(yield, 1, 2);
list($a, $b) = yield;

Same with:
call(yield $value) - what is the meaning of $value here? Why not just
call(yield)?

I would also not support array((yield $key => $value)) - it seems to be
really unclear how it works. I'd just have yield produce a value which
was sent, and that's it, and you could use this value in the same way
you'd use any other expression.

Only question I have here is what happens if you use yield in the middle
of function call expression, for example:
call(foo(bar(), $foo, yield, 3), baz());

We are kind of stopping the function in the middle of the function call
and going to unrelated code here, and may never return back. Where the
required cleanups, etc. should happen?

Another question is, if my function is like this:
function foo()
{
var_dump(yield);
}

And I do: foo()->current() - what happens? Do I get null there? What
happens if after that I do send() on the same generator? In general,
interaction between incoming and outgoing yields is not very clear,
especially what happens when you combine them.

Also, small point: yield is repeatably called "statement" in the doc,
but in fact it can be both statement and expression with returning yield.

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

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Andrew Faulds
Re: [PHP-DEV] Generators in PHP
August 08, 2012 11:00PM
On 08/08/12 21:43, Stas Malyshev wrote:
> Hi!
>
>
>> https://wiki.php.net/rfc/generators#yield_keyword
>> https://wiki.php.net/rfc/generators#sending_values
> I'm not sure $data = (yield $value) makes a lot of sense. Why we have
> two variables here? Why it is only operator in the language that
> requires parentheses around? All these complex parentheses rules seem
> unnecessarily complicated. I'd rather do it in a more simple way: yield
> in an expression means incoming yield. I.e.:
> $data = yield;
> foo(yield, 1, 2);
> list($a, $b) = yield;
>
> Same with:
> call(yield $value) - what is the meaning of $value here? Why not just
> call(yield)?
>
> I would also not support array((yield $key => $value)) - it seems to be
> really unclear how it works. I'd just have yield produce a value which
> was sent, and that's it, and you could use this value in the same way
> you'd use any other expression.
>
> Only question I have here is what happens if you use yield in the middle
> of function call expression, for example:
> call(foo(bar(), $foo, yield, 3), baz());
>
> We are kind of stopping the function in the middle of the function call
> and going to unrelated code here, and may never return back. Where the
> required cleanups, etc. should happen?
>
> Another question is, if my function is like this:
> function foo()
> {
> var_dump(yield);
> }
>
> And I do: foo()->current() - what happens? Do I get null there? What
> happens if after that I do send() on the same generator? In general,
> interaction between incoming and outgoing yields is not very clear,
> especially what happens when you combine them.
>
> Also, small point: yield is repeatably called "statement" in the doc,
> but in fact it can be both statement and expression with returning yield.
>
Hmm. This is just a quick thought:

Considering the yield syntax will vary about needing () round it, why
not make it a "fake" function (language construct).

This way it's consistent: yield(), yield($v), yield($k => $v), $a =
yield(), etc.

(yield $x) is just messy as an expression. We don't have (isset $x), we
have isset($x).

--
Andrew Faulds
http://ajf.me/


--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Larry Garfield
Re: [PHP-DEV] Re: Generators in PHP
August 09, 2012 04:30AM
On 07/27/2012 07:23 AM, Lester Caine wrote:
> Nikita Popov wrote:
>>> I'll ask again since no one has answered ...
>>> >
>>> >In a different way ...
>>> >Is the only thing that changes the 'function' into a 'generator'
>>> replacing
>>> >the call to process the data with 'yield'? ( That would be
>>> 'SUSPEND' in an
>>> >SQL procedure ) ...
>>> >
>>> >So how DOES an IDE work out the flow in order to correctly check that
>>> >variables are defined?
>>> >
>>> >As always, my IDE provides a lot of 'sexy' stuff so that I don't
>>> need to
>>> >have it built in to the language, and I still can't see how a lot
>>> of what is
>>> >being loaded in helps with performance which is the only thing that
>>> I am
>>> >interested in. Performance wise why is yield better than just directly
>>> >calling a function to handle the data?
>> Lester Caine and Alex Aulbach,
>>
>> may I ask you to continue this discussion in a separate thread? I am
>> really interested in constructive responses about the generator RFC,
>> but your discussion is generating a lot of noise, which makes it very
>> hard for me to pick out the few mails that are of interest to me.
>>
>> If you could open a new thread (like "Generator keyword") it would
>> help a lot.
>
> Nikita - I am looking for a well reasoned argument as to why generator
> has to be added at all! 'Just because it can be' is not a valid
> argument, but perhaps you could add to the RFC the performance
> implication or advantage of what is being proposed. That would at
> least be some comparison with the current methods of doing the same
> thing?
>

Anthony had a very good writeup on generators and how they compare to
iterators last week:

http://blog.ircmaxell.com/2012/07/what-generators-can-do-for-you.html

I think that does a good job of laying out the case for generators as
"low-effort iterators".

I still think the syntax feels clunky to me, but that's probably because
I'm not used to it from other languages.

One question, though: It looks based on the voting like finally {}
blocks are going in. So... what should happen in the following situation:

function stuff() {
try {
foreach (range(1, 100) as $i) {
yield $i;
}
}
finally {
print "All done";
}
}

Does "All done" get printed once, or 101 times? Similarly:

function things() {
$i = 1;
try {
while (true) {
yield $i++;
}
}
finally {
print "All done";
}
}

That will run indefinitely. So will "All done" ever print, or does that
finally become unreachable?

(I have no clue what the behavior "should" be in these cases, just that
it should be sorted out sooner rather than later.)

--Larry Garfield

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Sherif Ramadan
Re: [PHP-DEV] Re: Generators in PHP
August 09, 2012 05:00AM
>
> One question, though: It looks based on the voting like finally {} blocks
> are going in. So... what should happen in the following situation:
>
> function stuff() {
> try {
> foreach (range(1, 100) as $i) {
> yield $i;
> }
> }
> finally {
> print "All done";
> }
> }
>
> Does "All done" get printed once, or 101 times? Similarly:
>
> function things() {
> $i = 1;
> try {
> while (true) {
> yield $i++;
> }
> }
> finally {
> print "All done";
> }
> }
>
> That will run indefinitely. So will "All done" ever print, or does that
> finally become unreachable?
>
> (I have no clue what the behavior "should" be in these cases, just that it
> should be sorted out sooner rather than later.)
>


Based on my understanding of both RFCs, I don't see that this would be
a conflict or lead to unexpected behavior. As stated by the generators
RFC: "When you first call the generator function ($lines =
getLinesFromFile($fileName)) the passed argument is bound, but nothing
of the code is actually executed. Instead the function directly
returns a Generator object.".

So in the event we are calling the function stuff(), nothing should
actually be executed, and as we iterate over the traversable object
the generator should be "passing control back and forth between the
generator and the calling code". This would be indicated by the
"yield" keyword present in the function. Meaning you should see the
expected result and finally should only ever be called once in your
first example.

As for you second example... Obviously if you've created an infinite
loop you've made everything outside of the loop unreachable, whether
you're using generators or not.

However, I'll let the author of the RFC provide any clarification or
corrections where I might be wrong.

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Morgan L. Owens
Re: Re: [PHP-DEV] Re: Generators in PHP
August 09, 2012 05:40AM
On 2012-08-09 14:25, Larry Garfield wrote:
> On 07/27/2012 07:23 AM, Lester Caine wrote:
>>
>> Nikita - I am looking for a well reasoned argument as to why generator
>> has to be added at all! 'Just because it can be' is not a valid
>> argument, but perhaps you could add to the RFC the performance
>> implication or advantage of what is being proposed. That would at
>> least be some comparison with the current methods of doing the same
>> thing?
>>
>
> Anthony had a very good writeup on generators and how they compare to
> iterators last week:
>
> http://blog.ircmaxell.com/2012/07/what-generators-can-do-for-you.html
>
> I think that does a good job of laying out the case for generators as
> "low-effort iterators".
>
I for one am lazy, and would much prefer writing:
<?php
function append_iterator($first, $second)
{
foreach($first as $i)
{
yield $i;
}
foreach($second as $i)
{
yield $i;
}
}
?>

to

<?php
function append_iterator($first, $second)
{
if(is_object($first) && !($first instanceof Iterator))
{
$first = (array)$first;
}
if(is_array($first))
{
$first = new ArrayIterator($first);
}
if(is_object($second) && !($second instanceof Iterator))
{
$second = (array)$second;
}
if(is_array($second))
{
$second = new ArrayIterator($second);
}
return new myAppendIterator($first, $second);
}

class myAppendIterator implements Iterator
{
private $_state = 0;
private $_key;
private $_current;

private $first, $second;

public function __construct($first, $second)
{
$this->first = $first;
$this->second = $second;
}

public function rewind()
{
$this->first->rewind();
$this->second->rewind();
$this->state = 0;
}

public function current()
{
return $this->_current;
}

public function key()
{
return $this->_key;
}

public function next()
{
switch($this->_state)
{
case -1:
return;
case 0:
$this->first->rewind();
$this->_state = 1;
case 1:
if($this->first->valid())
{
$this->_current = $this->first->current();
$this->_key = $this->first->key();
$this->first->next();
return;
}
else
{
$this->second->rewind();
$this->_state = 2;
}
case 2:
if($this->second->valid())
{
$this->_current = $this->second->current();
$this->_key = $this->second->key();
$this->second->next();
return;
}
else
{
$this->_state = -1;
return;
}
}
}

public function valid()
{
return $this->_state != -1;
}
}
?>

>
> One question, though: It looks based on the voting like finally {}
> blocks are going in. So... what should happen in the following situation:
>
> function stuff() {
> try {
> foreach (range(1, 100) as $i) {
> yield $i;
> }
> }
> finally {
> print "All done";
> }
> }
>
> Does "All done" get printed once, or 101 times? Similarly:
>
> function things() {
> $i = 1;
> try {
> while (true) {
> yield $i++;
> }
> }
> finally {
> print "All done";
> }
> }
>
> That will run indefinitely. So will "All done" ever print, or does that
> finally become unreachable?
>
My own gut expectation (the answers that would surprise me the least)
would be (a) once (because the foreach is _inside_ the try block, and
the catch/finally handler is hence _outside_ the loop); and (b) the
finally{} is unreachable (assuming that loop doesn't manage to throw an
exception!) because control flow never gets to the end of the try block.


--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Morgan L. Owens
Re: Re: [PHP-DEV] Generators in PHP
August 09, 2012 06:00AM
On 2012-08-09 08:42, Nikita Popov wrote:
>
> Without parenthesis their behavior in array definitions and nested
> yields is ambigous:
>
> array(yield $key => $value)
> // can be either
> array((yield $key) => $value)
> // or
> array((yield $key => $value))
>
> yield yield $key => $value;
> // can be either
> yield (yield $key) => $value;
> // or
> yield (yield $key => $value);
>
> Apart from that particular case there is the general operator
> precedence inclarity, e.g.
>
> yield $foo . $bar;
> // could be
> (yield $foo) . $bar;
> // or
> yield ($foo . $bar);
>
Is this complicating yield a bit too much? All these ambiguities would
go away if 'yield' had the same grammatical status as 'return' - in
other words, if it were treated as a control-flow keyword rather than as
an operator.



--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Ford, Mike
RE: [PHP-DEV] Re: Generators in PHP
August 09, 2012 12:20PM
> -----Original Message-----
> From: Andrew Faulds [mailto:[email protected]]
> Sent: 25 July 2012 18:03

[...]

> Fact: Adding a new name for a special kind of function as a syntax
> construct is going to cost (possibly unnecessary) time and energy,
> because now you have functions, and weird things that look almost
> like
> functions but aren't and can only be used to make generators.

That looks to me like a perfect argument *in favour* of the
"generator" keyword! I'm a very literal kind of person, and I would
absolutely want "weird things that look almost like functions but
[...] can only be used to make generators" to be clearly labelled
as such without having to hunt through the body of the not-quite-
function.

The signposting needn't even be as in-your-face as a generator
keyword (either instead of or in addition to function): I could get
behind a variation such as:

function f($x, $y) yields {
...
yield $z;
...
}

Or even (stretching a bit to re-use an existing keyword!):

function f($x, $y) return {
...
yield $z;
...
}

Although I like the concept of generators, I would be -1 for any
implementation that doesn't differentiate them in some way from
regular functions.

Cheers!

Mike

--
Mike Ford,
Electronic Information Developer, Libraries and Learning Innovation,
Portland PD507, City Campus, Leeds Metropolitan University,
Portland Way, LEEDS,  LS1 3HE,  United Kingdom
E: m.ford@leedsmet.ac.uk T: +44 113 812 4730




To view the terms under which this email is distributed, please go to http://disclaimer.leedsmet.ac.uk/email.htm

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Nikita Popov
Re: [PHP-DEV] Generators in PHP
August 09, 2012 06:00PM
On Wed, Aug 8, 2012 at 10:55 PM, Andrew Faulds <[email protected]> wrote:
> Hmm. This is just a quick thought:
>
> Considering the yield syntax will vary about needing () round it, why not
> make it a "fake" function (language construct).
>
> This way it's consistent: yield(), yield($v), yield($k => $v), $a = yield(),
> etc.
>
> (yield $x) is just messy as an expression. We don't have (isset $x), we have
> isset($x).

There are two reasons why I would not choose that syntax:

1. This would make "yield" look like a functions, without actually
being a function. PHP has done this in the past quite often and I
think it was a mistake. It is the reason why people try to write
empty(someCall()). It looks like a function, so they expect it to
behave like one. Similarly you also can't do $f = 'empty'; $f($foo),
which again is confusing to people new to the language.

2. Other languages that implement generators also use the "yield $foo"
syntax (and also have the same parentheses requirements). So this
makes PHP consistent with them.

Nikita

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Andrew Faulds
Re: [PHP-DEV] Generators in PHP
August 09, 2012 06:10PM
On 09/08/12 16:58, Nikita Popov wrote:
> On Wed, Aug 8, 2012 at 10:55 PM, Andrew Faulds <[email protected]> wrote:
>> Hmm. This is just a quick thought:
>>
>> Considering the yield syntax will vary about needing () round it, why not
>> make it a "fake" function (language construct).
>>
>> This way it's consistent: yield(), yield($v), yield($k => $v), $a = yield(),
>> etc.
>>
>> (yield $x) is just messy as an expression. We don't have (isset $x), we have
>> isset($x).
> There are two reasons why I would not choose that syntax:
>
> 1. This would make "yield" look like a functions, without actually
> being a function. PHP has done this in the past quite often and I
> think it was a mistake. It is the reason why people try to write
> empty(someCall()). It looks like a function, so they expect it to
> behave like one. Similarly you also can't do $f = 'empty'; $f($foo),
> which again is confusing to people new to the language.
yield(), so far as the programmer is concerned, might as well be a
function. It isn't, strictly speaking, but like other function calls, it
suspends execution of the function until the called function completes.

So I don't think this is a problem.
> 2. Other languages that implement generators also use the "yield $foo"
> syntax (and also have the same parentheses requirements). So this
> makes PHP consistent with them.
Doesn't mean we can't lead the way and have a nicer syntax :)
> Nikita


--
Andrew Faulds
http://ajf.me/


--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Nikita Popov
Re: [PHP-DEV] Generators in PHP
August 09, 2012 07:50PM
On Thu, Aug 9, 2012 at 5:59 PM, Andrew Faulds <[email protected]> wrote:
> yield(), so far as the programmer is concerned, might as well be a function.
> It isn't, strictly speaking, but like other function calls, it suspends
> execution of the function until the called function completes.
>
> So I don't think this is a problem.

I definitely can see that yield() is a plausible syntax, but I still
think that the normal yield notation is better suited. Another reason
is the following:

PHP has several constructs with a yield-like syntax. Examples are
"include $file" (and require etc) and "print $string". Both are
keyword expressions, so they are rather similar to yield.

On the other hand there are also other constructs which use the
function notation. Like isset(), empty(), etc.

The main distinction between them is their primary use: Both include
and print are usually used as statements, not as expressions. Writing
$foo = include "bar"; is something you rarely do. So here the
friendlier keyword notation is chosen. isset() and empty() on the
other hand are pretty much always used as expressions (they really
don't make sense as statements). So the function-like syntax is chosen
here, because it causes less ambiguities.

For yield the same applies. Yield will be primarily used as a
statement. The vast majority of cases will just be

yield $someValue;

The expression use is just secondary here. And even there the
value-less yield (the one that does not need parens) is the most
common case:

$data = yield;

The case where you want to both receive and send at the same time is
rather rare (mostly for trampoline patterns or task scheduling).

So I think we should not sacrifice the most common cases for slightly
better syntax in the rare cases.

Also, currently yield looks very similar to return and I think this is
a nice thing as it is similar semantically. yield($foo) would give it
different semantics, imho.

Nikita

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
hakre
Re: [PHP-DEV] Generators in PHP
August 09, 2012 11:30PM
> Also, currently yield looks very similar to return and I think this is
> a nice thing as it is similar semantically. yield($foo) would give it
> different semantics, imho.

I love this point a lot. Return is very common and yield is some kind of return.


--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Morgan L. Owens
Re: [PHP-DEV] Generators in PHP
August 10, 2012 03:00AM
Mike Ford wrote:
>
> The signposting needn't even be as in-your-face as a generator
> keyword (either instead of or in addition to function): I could get
> behind a variation such as:
>
> function f($x, $y) yields { ... yield $z; ... }
>
> Or even (stretching a bit to re-use an existing keyword!):
>
> function f($x, $y) return { ... yield $z; ... }
>
> Although I like the concept of generators, I would be -1 for any
> implementation that doesn't differentiate them in some way from
> regular functions.
>
In other words you want to have return-value type-hinting _in one
specific instance_: when calling f() returns a Generator object.



--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Morgan L. Owens
Re: [PHP-DEV] Generators in PHP
August 10, 2012 03:10AM
hakre wrote:

>> Also, currently yield looks very similar to return and I think this
>> is a nice thing as it is similar semantically. yield($foo) would
>> give it different semantics, imho.
>
> I love this point a lot. Return is very common and yield is some
> kind of return.
>
I agree also: yield behaves far more like a return statement than a
function call. (In a sense, a yield is a return that doesn't discard the
current function's execution state.)

Which (as it happens) reminds me of the difference between
return $foo;
and
return ($foo);

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Nikita Popov
Re: [PHP-DEV] Re: Generators in PHP
August 18, 2012 03:40PM
On Thu, Aug 9, 2012 at 4:49 AM, Sherif Ramadan <[email protected]> wrote:
>>
>> One question, though: It looks based on the voting like finally {} blocks
>> are going in. So... what should happen in the following situation:
>>
>> function stuff() {
>> try {
>> foreach (range(1, 100) as $i) {
>> yield $i;
>> }
>> }
>> finally {
>> print "All done";
>> }
>> }
>>
>> Does "All done" get printed once, or 101 times? Similarly:
>>
>> function things() {
>> $i = 1;
>> try {
>> while (true) {
>> yield $i++;
>> }
>> }
>> finally {
>> print "All done";
>> }
>> }
>>
>> That will run indefinitely. So will "All done" ever print, or does that
>> finally become unreachable?
>>
>> (I have no clue what the behavior "should" be in these cases, just that it
>> should be sorted out sooner rather than later.)
>>
>
>
> Based on my understanding of both RFCs, I don't see that this would be
> a conflict or lead to unexpected behavior. As stated by the generators
> RFC: "When you first call the generator function ($lines =
> getLinesFromFile($fileName)) the passed argument is bound, but nothing
> of the code is actually executed. Instead the function directly
> returns a Generator object.".
>
> So in the event we are calling the function stuff(), nothing should
> actually be executed, and as we iterate over the traversable object
> the generator should be "passing control back and forth between the
> generator and the calling code". This would be indicated by the
> "yield" keyword present in the function. Meaning you should see the
> expected result and finally should only ever be called once in your
> first example.
>
> As for you second example... Obviously if you've created an infinite
> loop you've made everything outside of the loop unreachable, whether
> you're using generators or not.
>
> However, I'll let the author of the RFC provide any clarification or
> corrections where I might be wrong.

Yes, that is basically right. The one interesting case that arises
when using generators is what happens when you close a generator
before it is finished. E.g. in the function mentioned above:

function stuff() {
try {
foreach (range(1, 100) as $i) {
yield $i;
}
} finally {
print "All done";
}
}

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Nikita Popov
Re: [PHP-DEV] Re: Generators in PHP
August 18, 2012 03:40PM
On Thu, Aug 9, 2012 at 4:49 AM, Sherif Ramadan <[email protected]> wrote:
>>
>> One question, though: It looks based on the voting like finally {} blocks
>> are going in. So... what should happen in the following situation:
>>
>> function stuff() {
>> try {
>> foreach (range(1, 100) as $i) {
>> yield $i;
>> }
>> }
>> finally {
>> print "All done";
>> }
>> }
>>
>> Does "All done" get printed once, or 101 times? Similarly:
>>
>> function things() {
>> $i = 1;
>> try {
>> while (true) {
>> yield $i++;
>> }
>> }
>> finally {
>> print "All done";
>> }
>> }
>>
>> That will run indefinitely. So will "All done" ever print, or does that
>> finally become unreachable?
>>
>> (I have no clue what the behavior "should" be in these cases, just that it
>> should be sorted out sooner rather than later.)
>>
>
>
> Based on my understanding of both RFCs, I don't see that this would be
> a conflict or lead to unexpected behavior. As stated by the generators
> RFC: "When you first call the generator function ($lines =
> getLinesFromFile($fileName)) the passed argument is bound, but nothing
> of the code is actually executed. Instead the function directly
> returns a Generator object.".
>
> So in the event we are calling the function stuff(), nothing should
> actually be executed, and as we iterate over the traversable object
> the generator should be "passing control back and forth between the
> generator and the calling code". This would be indicated by the
> "yield" keyword present in the function. Meaning you should see the
> expected result and finally should only ever be called once in your
> first example.
>
> As for you second example... Obviously if you've created an infinite
> loop you've made everything outside of the loop unreachable, whether
> you're using generators or not.
>
> However, I'll let the author of the RFC provide any clarification or
> corrections where I might be wrong.

Ooops, accidentially hit "Send" too early. So again:

Yes, that is basically right. The one interesting case that arises
when using generators is what happens when you close a generator
before it is finished. E.g. in the function mentioned above:

function stuff() {
try {
foreach (range(1, 100) as $i) {
yield $i;
}
} finally {
print "All done";
}
}

If you now create the generator, do 50 iterations and then close it:

$gen = stuff();
foreach (stuff() as $i) {
if ($i == 50) break;
}
unset($gen);

In that case the code (normally) would never reach the "finally"
clause, simply because it isn't resumed anymore.

Python solved this problem by throwing a GeneratorExitException into
the generator when it is closed. So when unset($gen) is called an
exception is thrown at the yield-stackframe and the generator resumed.
This then invokes usual exception bubbling and runs finally clauses.
When the GeneratorExitException bubbles out of the generator function
is implicitly caught and discarded.

For PHP we would need to have some similar behavior. PHP's current
exception model is incompatible with GeneratorExitException (because
PHP does not have BaseExceptions). So what I'd probably do instead is
monkeypatch a ZEND_RETURN opcode at the current execution position and
resume the generator. This would then invoke any finally clauses
through the usual processes. This should be a very transparent process
that "just works" without the need for any strange exception hacks.

Nikita

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Stas Malyshev
Re: [PHP-DEV] Re: Generators in PHP
August 19, 2012 06:30AM
Hi!

> For PHP we would need to have some similar behavior. PHP's current
> exception model is incompatible with GeneratorExitException (because
> PHP does not have BaseExceptions). So what I'd probably do instead is
> monkeypatch a ZEND_RETURN opcode at the current execution position and

Patching opcodes is not a good idea, since opcodes could be cached, and
the cache can be shared between different processes.
--
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Nikita Popov
Re: [PHP-DEV] Re: Generators in PHP
August 19, 2012 11:00AM
On Sun, Aug 19, 2012 at 6:28 AM, Stas Malyshev <[email protected]> wrote:
> Hi!
>
>> For PHP we would need to have some similar behavior. PHP's current
>> exception model is incompatible with GeneratorExitException (because
>> PHP does not have BaseExceptions). So what I'd probably do instead is
>> monkeypatch a ZEND_RETURN opcode at the current execution position and
>
> Patching opcodes is not a good idea, since opcodes could be cached, and
> the cache can be shared between different processes.

Patching a single opcode should be okay, because it does not require
modification of the op_array. Only execute_data->opline has to be
changed (and the execute data is not shared). The exception handling
mechanism currently uses the same technique. It patches an
ZEND_HANDLE_EXCEPTION opcode into the current position.

Nikita

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Derick Rethans
Re: [PHP-DEV] Re: Generators in PHP
August 19, 2012 06:10PM
On Sun, 19 Aug 2012, Nikita Popov wrote:

> On Sun, Aug 19, 2012 at 6:28 AM, Stas Malyshev <[email protected]> wrote:
> >
> >> For PHP we would need to have some similar behavior. PHP's current
> >> exception model is incompatible with GeneratorExitException
> >> (because PHP does not have BaseExceptions). So what I'd probably do
> >> instead is monkeypatch a ZEND_RETURN opcode at the current
> >> execution position and
> >
> > Patching opcodes is not a good idea, since opcodes could be cached,
> > and the cache can be shared between different processes.
>
> Patching a single opcode should be okay, because it does not require
> modification of the op_array. Only execute_data->opline has to be
> changed (and the execute data is not shared). The exception handling
> mechanism currently uses the same technique. It patches an
> ZEND_HANDLE_EXCEPTION opcode into the current position.

Please do not modify opcodes for running code. That is a ginormous hack
and it *will* cause problems with some code.

Derick

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

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Andrew Faulds
Re: [PHP-DEV] Re: Generators in PHP
August 19, 2012 06:10PM
On 19/08/12 17:03, Derick Rethans wrote:
> On Sun, 19 Aug 2012, Nikita Popov wrote:
>
>> On Sun, Aug 19, 2012 at 6:28 AM, Stas Malyshev <[email protected]> wrote:
>>>> For PHP we would need to have some similar behavior. PHP's current
>>>> exception model is incompatible with GeneratorExitException
>>>> (because PHP does not have BaseExceptions). So what I'd probably do
>>>> instead is monkeypatch a ZEND_RETURN opcode at the current
>>>> execution position and
>>> Patching opcodes is not a good idea, since opcodes could be cached,
>>> and the cache can be shared between different processes.
>> Patching a single opcode should be okay, because it does not require
>> modification of the op_array. Only execute_data->opline has to be
>> changed (and the execute data is not shared). The exception handling
>> mechanism currently uses the same technique. It patches an
>> ZEND_HANDLE_EXCEPTION opcode into the current position.
> Please do not modify opcodes for running code. That is a ginormous hack
> and it *will* cause problems with some code.
I can't say much about this in particular, but using hacks and kludges
to implement language features is probably not the best of ideas.
> Derick
>


--
Andrew Faulds
http://ajf.me/


--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Nikita Popov
Re: [PHP-DEV] Re: Generators in PHP
August 19, 2012 08:20PM
On Sun, Aug 19, 2012 at 6:03 PM, Derick Rethans <[email protected]> wrote:
> On Sun, 19 Aug 2012, Nikita Popov wrote:
>
>> On Sun, Aug 19, 2012 at 6:28 AM, Stas Malyshev <[email protected]> wrote:
>> >
>> >> For PHP we would need to have some similar behavior. PHP's current
>> >> exception model is incompatible with GeneratorExitException
>> >> (because PHP does not have BaseExceptions). So what I'd probably do
>> >> instead is monkeypatch a ZEND_RETURN opcode at the current
>> >> execution position and
>> >
>> > Patching opcodes is not a good idea, since opcodes could be cached,
>> > and the cache can be shared between different processes.
>>
>> Patching a single opcode should be okay, because it does not require
>> modification of the op_array. Only execute_data->opline has to be
>> changed (and the execute data is not shared). The exception handling
>> mechanism currently uses the same technique. It patches an
>> ZEND_HANDLE_EXCEPTION opcode into the current position.
>
> Please do not modify opcodes for running code. That is a ginormous hack
> and it *will* cause problems with some code.

I don't see how it can cause problems (exception handling works after
all), but I agree that it's better to avoid. Thinking about it again,
there shouldn't be necessity for it. But right now I'm waiting for
laruence to refactor the finally handling code. After that's done I'll
see how to best implement it ;)

Nikita

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Sorry, only registered users may post in this forum.

Click here to login