pecan is a macro-based library for Haxe that lets you write coroutines, which are functions that can suspend their execution at any point:
var co = pecan.Co.co({
while (true)
trace('Hello, ${accept()}!');
}, (_ : String)).run();
co.give("Haxe"); // Hello, Haxe!
co.give("community"); // Hello, community!
It has been around for some time, but I recently rewrote it from scratch, this time basing the transformation around the typed AST instead of the untyped one – as a result, more Haxe syntax is supported, type inference works better, and more.
I’ve also moved the documentation into a nicer manual form.
A summary of the features:
suspendable functions written using regular Haxe syntax
input and output using accept() and yield(...), in addition to arguments and return values
simple way to define suspending functions, e.g. for interop with Promises
shorthand syntax for immediately invoked coroutines and class field-like coroutines
macro-based, no dependencies on other libraries or plugins
labels and goto
I find coroutines incredibly useful for certain kinds of game logic and user interaction in general. Hopefully you find this useful as well!
I must say I really like this lower-level coroutine implementation approach, as opposed to the popular high-level promise-powered async/await libraries. It enables different use-cases for coroutines, including async/await for different kinds of promise/future primitives as well as different kinds of scripting DSLs like animations or game logic. Very much in the spirit of my initial coroutine proposal for Haxe
I also think that this is a very important research/exploration/proof-of-concept work for the Haxe’s own coroutines that will eventually come, great job! But what’s especially great is that it’s already here and ready-to-use. I’m going to play with it a bit more and come back with some usage snippets, my first one would be async/await helpers for tink.core Futures.
Thank you, Aurel! Great work not just with code, but also with documentation!
I’m going to play with it a bit more and come back with some usage snippets, my first one would be async/await helpers for tink.core Futures.
Looking forward to this, Dan. The concept of “interacting with code inside a function” seems a little strange (.give(), .take(). .wakeup() etc). Hard for me to immediately see how this maps to common async use cases, so examples where async/await are handled are very welcome!
and sorry another question. how is complex the serialization of a coroutine? i imagine the use of a coroutine for example in stateless enviroment as php. that every time request can resume the state and position of a coroutine?
A timer example is in the manual here, if you meant “suspend this coroutine for X milliseconds”.
As for the serialisation, this is not currently possible, but implementing it will be relatively straight-forward. I opened an issue, will get back to this soon.
Hey, here’s what I have so far. The await is just a @:pecan.accept method that adds the given callback as a future handler. And async is a macro that takes a coroutine expression and wraps it into new Future. It’s currently missing some cleanups (like unregistering the await handlers when the coroutine terminates), but it seems to work.
So I don’t really use any of the coroutine API from inside the coroutine itself and I discussed with Aurel that there should be some way to actually not expose it, since it’s not needed for async/await and is can actually break the coroutine, since there’s no real scheduler/runner in this scenario.
After watching the stream from Shiro Games recently (can’t find it anymore on twitch but found it on youtube with nice chapters Performance Haxe in "Dune: Spice Wars" - Tom Rethaller - YouTube) I wanted to try using pecan to make some functions async using await() but I can’t seem to get it to work… some help would be much appreciated !
When I try the example from the manual ( pecan.CoTools – pecan manual ) I get this error : pecan.instances.CoInstance_0 has no field await
Here is my code, what am I missing ?
package;
import openfl.display.Sprite;
import pecan.Syntax;
class Main extends Sprite implements Syntax
{
public function new()
{
super();
var a = pecan.Co.co(function():String {
suspend();
return "Haxe";
}).run();
var b = pecan.Co.co({
var result = a.await();
trace('Hello, $result!');
}).run();
a.wakeup(); // output: Hello, Haxe!
}
}