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
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
On Mon, Dec 17, 2012 at 11:03 PM, Stas Malyshev smalyshev@sugarcrm.comwrote:
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 howCould 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?
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
On Mon, Dec 17, 2012 at 11:03 PM, Stas Malyshev smalyshev@sugarcrm.comwrote:
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 howCould 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?
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
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
On Fri, Dec 21, 2012 at 11:09 PM, Christopher Jones <
christopher.jones@oracle.com> wrote:
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
Committed in
https://github.com/php/php-src/commit/be7b0bc3ec02e4f223920ee6397f9c4993eb7df5
.
I also adjusted the RFC to list this :)
Nikita
Hi,
On Tue, Dec 18, 2012 at 6:03 AM, Stas Malyshev smalyshev@sugarcrm.comwrote:
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 howCould 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
:)
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--
--
Tjerk
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
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