On Thu, Aug 9, 2012 at 4:49 AM, Sherif Ramadan <theanomaly...@gmail.com> 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

Reply via email to