# Adding Generator::throw()

- Thread: https://externals.io/message/64325
- Original archive: https://news-web.php.net/php.internals/64325
- Started by Nikita Popov on 2012-12-17T21:29:54+00:00
- Messages: 9
- 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

---

## #64325 — Nikita Popov — 2012-12-17T21:29:54+00:00

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

Hey internals!

I would like to add a ->throw() method to generator objects and as this
wasn't part of the proposal that was voted on I'd like to ask back first.

The Generator::throw(Exception $exception) method takes an exception and
throws it at the current interruption point in the generator. It basically
behaves as if you replaced the current yield statement with a throw
statement and resumed the generator.

This method is also part of Python's generator implementation (as well as
ECMAScript's). I decided not to implement it at first, because I wasn't
convinced of its usefulness. But after some further consideration adding it
seems to make more sense.

Basically the method allows you to do delegate error handling to the
coroutine, rather than doing it yourself (as you are not always able to do
it). It is particularly useful in more complicated settings, e.g. if you
are doing task scheduling through coroutines. For a small sample of how
this looks like see http://taskjs.org/. What the ->throw() method would do
in these examples is that it allows to check for errors by try/catching the
yield statement (rather than going for some odd solution with error
callbacks).

Here is a patch that would add this functionality:
https://github.com/nikic/php-src/commit/b16e29fea6cba576d4176524bf43d6e7d00f45fa

Anyone against adding this?
Nikita

---

## #64326 — Stas Malyshev — 2012-12-17T22:03:09+00:00

URL: https://externals.io/message/64325#64326 · In reply to: #64325

Hi!

> Basically the method allows you to do delegate error handling to the
> coroutine, rather than doing it yourself (as you are not always able to do
> it). It is particularly useful in more complicated settings, e.g. if you
> are doing task scheduling through coroutines. For a small sample of how

Could you expand on this point a bit more? It sounds like using
exceptions for flow control, which is usually a very bad idea.

> this looks like see http://taskjs.org/. What the ->throw() method would do
> in these examples is that it allows to check for errors by try/catching the
> yield statement (rather than going for some odd solution with error
> callbacks).

Could you point to some specific example?

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

---

## #64328 — Nikita Popov — 2012-12-17T22:45:04+00:00

URL: https://externals.io/message/64325#64328 · In reply to: #64326

On Mon, Dec 17, 2012 at 11:03 PM, Stas Malyshev <smalyshev@sugarcrm.com>wrote:

> Hi!
>
> > […]
> do
> > […]
>
> Could you expand on this point a bit more? It sounds like using
> exceptions for flow control, which is usually a very bad idea.
>
> > […]
> do
> > […]
> the
> > […]
>
> Could you point to some specific example?
>
The basic idea behind this kind of task management is that you can do
asynchronous operations just like you would synchronous ones, i.e. without
the need for callbacks or wait-loops. Whenever the functions wants to
perform some async action it doesn't directly do it, rather it yields the
operation that does it. The scheduler then waits until that operation is
finished (running other tasks in the meantime) and only resumes the
coroutine once it is finished. This way you can write async code without
the ugliness that is usually involved with async code.

Here is an example to get a rough idea of how the use looks like:

function server() {
    // ...
    $stuff = yield $socket->recv();
    // ...
    yield $socket->send($response);
    // ...
}

The throw() method comes in when you want to handle errors on those
asynchronous operations. Without it you would be forced to use error codes.
throw() allows you to do the error handling just like you would normally
do. E.g. consider that $socket->send() were a fallible operation. Then you
could catch errors like this:

function server() {
    // ...
    try {
        yield $socket->send($response);
    } catch (SocketException $e) {
        // do some error handling
    }
    // ...
}

In this case the outside code (where the exception comes from) can't know
what it should do in case of an error. Only the code in the coroutine knows
that. That's why you need some possibility to throw the error into the
context that knows how to deal with it.

I hope that this makes it a bit more clear.

Nikita

---

## #64412 — Nikita Popov — 2012-12-21T14:54:40+00:00

URL: https://externals.io/message/64325#64412 · In reply to: #64328

> On Mon, Dec 17, 2012 at 11:03 PM, Stas Malyshev <smalyshev@sugarcrm.com>wrote:
>
> > […]
> The basic idea behind this kind of task management is that you can do
> asynchronous operations just like you would synchronous ones, i.e. without
> the need for callbacks or wait-loops. Whenever the functions wants to
> perform some async action it doesn't directly do it, rather it yields the
> operation that does it. The scheduler then waits until that operation is
> finished (running other tasks in the meantime) and only resumes the
> coroutine once it is finished. This way you can write async code without
> the ugliness that is usually involved with async code.
>
> Here is an example to get a rough idea of how the use looks like:
>
> function server() {
>     // ...
>     $stuff = yield $socket->recv();
>     // ...
>     yield $socket->send($response);
>     // ...
> }
>
> The throw() method comes in when you want to handle errors on those
> asynchronous operations. Without it you would be forced to use error codes.
> throw() allows you to do the error handling just like you would normally
> do. E.g. consider that $socket->send() were a fallible operation. Then you
> could catch errors like this:
>
> function server() {
>     // ...
>     try {
>         yield $socket->send($response);
>     } catch (SocketException $e) {
>         // do some error handling
>     }
>     // ...
> }
>
> In this case the outside code (where the exception comes from) can't know
> what it should do in case of an error. Only the code in the coroutine knows
> that. That's why you need some possibility to throw the error into the
> context that knows how to deal with it.
>
> I hope that this makes it a bit more clear.
>
> Nikita
>

If there are no further objections I'll commit this tomorrow.

Nikita

---

## #64413 — Christopher Jones — 2012-12-21T22:09:59+00:00

URL: https://externals.io/message/64325#64413 · In reply to: #64412

> If there are no further objections I'll commit this tomorrow.
>
> Nikita

This feature needs documenting in Generator RFC before it is committed.

The RFC will be referenced by people new to the feature and so it
should have a complete list of changes.

Thanks,

Chris

-- 
christopher.jones@oracle.com  http://twitter.com/ghrd

---

## #64416 — Nikita Popov — 2012-12-23T23:49:30+00:00

URL: https://externals.io/message/64325#64416 · In reply to: #64413

On Fri, Dec 21, 2012 at 11:09 PM, Christopher Jones <
christopher.jones@oracle.com> wrote:

>
>
> 
>
> > […]
>
> This feature needs documenting in Generator RFC before it is committed.
>
> The RFC will be referenced by people new to the feature and so it
> should have a complete list of changes.
>
> Thanks,
>
> Chris
>

Committed in
https://github.com/php/php-src/commit/be7b0bc3ec02e4f223920ee6397f9c4993eb7df5
.

I also adjusted the RFC to list this :)

Nikita

---

## #64420 — Tjerk Anne Meesters — 2012-12-24T05:03:22+00:00

URL: https://externals.io/message/64325#64420 · In reply to: #64326

Hi,

On Tue, Dec 18, 2012 at 6:03 AM, Stas Malyshev <smalyshev@sugarcrm.com>wrote:

> Hi!
>
> > […]
> do
> > […]
>
> Could you expand on this point a bit more? It sounds like using
> exceptions for flow control, which is usually a very bad idea.
>

I've been hearing this argument from time to time and I don't understand
it; aren't exceptions created with the sole purpose of (error) flow control?

If so, then how can "exceptions for flow control" and "very bad idea" be
mentioned in the same sentence unless there are particular bad use cases
that are implicitly referred to when saying this?

If not, then perhaps my understanding of exceptions needs a spring cleaning
:)


>
> > […]
> do
> > […]
> the
> > […]
>
> Could you point to some specific example?
>
> --
> Stanislav Malyshev, Software Architect
> SugarCRM: http://www.sugarcrm.com/
> (408)454-6900 ext. 227
>
> --
> 
> 
>
>


-- 
--
Tjerk

---

## #64423 — Anthony Ferrara — 2012-12-24T12:09:36+00:00

URL: https://externals.io/message/64325#64423 · In reply to: #64420

Tjerk,

I've been hearing this argument from time to time and I don't understand
> it; aren't exceptions created with the sole purpose of (error) flow
> control?
>
> If so, then how can "exceptions for flow control" and "very bad idea" be
> mentioned in the same sentence unless there are particular bad use cases
> that are implicitly referred to when saying this?
>
> If not, then perhaps my understanding of exceptions needs a spring cleaning
> :)


Correct, Exceptions aren't supposed to be used for flow control.

However, this isn't about flow control. This is about throwing the
exception in the proper context. With generators and coroutines, the code
that throws the exception may not actually be a child of the code that
called it (from the stack's perspective). Therefore, adding
Generator::throw() simply allows for throwing the exception in the proper
stack frame (the framew that called the original code).

For an example of how this would be useful, read this *awesome* post about
coroutines:
http://nikic.github.com/2012/12/22/Cooperative-multitasking-using-coroutines-in-PHP.html

Anthony

---

## #64432 — Stas Malyshev — 2012-12-26T00:03:53+00:00

URL: https://externals.io/message/64325#64432 · In reply to: #64420

Hi!

> I've been hearing this argument from time to time and I don't understand
> it; aren't exceptions created with the sole purpose of (error) flow control?

No. Exceptions are meant for something that "should never happen". I.e.,
if your application reads its config files and they are not there, or
connects to the database and database is not there - that's an
exception. If you just have something like "user clicked this button and
not that button" - it should not be an exception. Thus, exceptions
should be used to handle cases which are not part of the normal program
flow control.

That said, it does not look like in this case (generators) it is being
used wrongly, my initial impression was based on the lack of
information, and turns out to be wrong.
-- 
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
