Any language with lambdas (that can be fired when an async completes) can simulate coroutines.
Assume we have some async function call :
future
which send the integer off over the net (or whatever) and eventually gets a result back. Assume
that future<> has a "AndThen" which schedules a function to run when it's done.
<
int> AsyncFunc( int x );
Then you can write a sequence of operations like :
future
with a little munging we can make it look more like a standard coroutine :
<
int> MySequenceOfOps( int x1 )
{
x1++;
future<
int> f1 = AsyncFunc(x1);
return f1.AndThen( [](int x2){
x2 *= 2;
future<
int> f2 = AsyncFunc(x2);
return f2.AndThen( [](int x3){
x3 --;
return x3;
} );
} );
}
#define YIELD(future,args) return future.AndThen( [](args){
future
the only really ugly bit is that you have to put a bunch of scope-closers at the end to match the number of
yields.
<
int> MySequenceOfOps( int x1 )
{
x1++;
future<
int> f1 = AsyncFunc(x1);
YIELD(f1,int x2)
x2 *= 2;
future<
int> f2 = AsyncFunc(x2);
YIELD(f2,int x3)
x3 --;
return x3;
} );
} );
}
This is really what any coroutine is doing under the hood. When you hit a "yield", what it does is take the remainder of the function and package that up as a functor to get called after the async op that you're yielding on is done.
Coroutines from lambdas have a few disadvantages, aside from the scope-closers annoyance. It's ugly to do
anything but simple linear control flow. The above example is the very simple case of "imperative, yield, imperative,
yield" , but in real code you want to have things like :
if ( bool )
{
YIELD
}
or
while ( some condition )
{
YIELD
}
which while probably possible with lambda-coroutines, gets ugly.
An advantage of lambda-coroutines is if you're in a language where you have lambdas with variable-capture, then you get that in your coroutines.
3 comments:
See also: Continuation-passing-style, and all async functions in node.js.
Yup. My only point here really is that they are mostly equivalent.
Holy cow though I find CPS code to be impossible to read and impossible to debug.
We're getting more and more of it in the world and good god is it ugly.
Callbacks are a nasty nasty way to write code.
Writing code in Go is the most fun I've had programming in years, mostly because of the way it handles these concurrency issues without dropping into callback hell.
Post a Comment