I just implemented continuation for Haxe.

269 views
Skip to first unread message

pop....@gmail.com

unread,
Aug 12, 2012, 1:58:14 PM8/12/12
to haxe...@googlegroups.com

Hello, I am glad to introduce my work this weekend: continuation for Haxe.

The project is at https://github.com/Atry/haxe-continuation.

Usage

If a function's last parameter is a callback function, it is async function. Use Continuation.cpsFunction to write a async function. InContinuation.cpsFunctionasync is a keyword to invoke other async functions.

import com.dongxiguo.continuation.Continuation;
class Sample 
{
  static var sleepOneSecond = callback(haxe.Timer.delay, _, 1000);
  public static function main() 
  {
    Continuation.cpsFunction(function asyncTest():Void
    {
      trace("Start continuation.");
      for (i in 0...10)
      {
        async(sleepOneSecond);
        trace("Run sleepOneSecond " + i + " times.");
      }
      trace("Continuation is done.");
    });
    asyncTest(function()
    {
      trace("Handler without continuation.");
    });
  }
}

Installation

I have upload to haxelib. To install, type following command in shell:
haxelib install continuation

pop....@gmail.com

unread,
Aug 12, 2012, 2:27:06 PM8/12/12
to haxe...@googlegroups.com, pop....@gmail.com
I guess the NodeJS guys may interest on this project.

pop....@gmail.com

unread,
Aug 13, 2012, 1:55:39 AM8/13/12
to haxe...@googlegroups.com, pop....@gmail.com
There is some former discussion at http://haxe.org/forum/thread/3395 

Cambiata

unread,
Aug 13, 2012, 3:35:17 AM8/13/12
to haxe...@googlegroups.com, pop....@gmail.com
Hi pop!

I'm sure it's great! :-)
But - in short - what kind of everyday problem does it solve..?

/ Jonas

pop....@gmail.com

unread,
Aug 13, 2012, 4:05:00 AM8/13/12
to haxe...@googlegroups.com, pop....@gmail.com
It kills a bulk of indentation when you invoke asynchronous function, especially when you work with NodeJS.

Before you use continuation, you have to write many code like this:
            });
         
});
        });
      });
    });
  });
});

And now it's gone.


在 2012年8月13日星期一UTC+8下午3时35分17秒,Cambiata写道:

Raoul Duke

unread,
Aug 13, 2012, 9:06:29 AM8/13/12
to haxe...@googlegroups.com
On Mon, Aug 13, 2012 at 12:35 AM, Cambiata <jona...@gmail.com> wrote:
> I'm sure it's great! :-)
> But - in short - what kind of everyday problem does it solve..?

cps can also be used for doing a form of threads, iiuc.

Alex Hoyau

unread,
Aug 14, 2012, 4:12:26 AM8/14/12
to haxe...@googlegroups.com
This is great!!
It's written with macros?

I did a test to use it as a kind of multithreading and it works great!

import com.dongxiguo.continuation.Continuation;
import js.Dom;
import js.Lib;

class Main
{
  static var handleActivity = callback(haxe.Timer.delay, _, 1000);
  public static function main() 
  {
    Lib.document.onkeypress = displayActivity;
    //(new haxe.Timer(1000)).run = displayActivity;
    Continuation.cpsFunction(function asyncTest(e:Event=null):Void
    {
      for (i in 0...1000)
      {
        handleActivity().async();
        displayMessage("Computing in the background ("+(i++)+")! Press any key to generate activity in the foreground...");
      }
    });
  }
  public static function displayActivity(e=null):Void {
      displayMessage("Activity in the foreground!!!!");
  }
  public static function displayMessage(msg:String):Void {
    var node = Lib.document.createElement("p");
    node.innerHTML = msg;
    Lib.document.body.appendChild(node);

杨博

unread,
Aug 14, 2012, 9:54:24 AM8/14/12
to haxe...@googlegroups.com
在 2012年8月14日星期二UTC+8下午4时12分26秒,Alex Hoyau写道:
This is great!!
It's written with macros?
Yes, it's written in macro.

You may notice the asynchronous call syntax is now .async() postfix. I changed it to fit the origin discussion.

杨博

unread,
Aug 14, 2012, 10:14:14 AM8/14/12
to haxe...@googlegroups.com
Look at the example, which use hx-node and continuation together.
The hx-node can even return multiply values! I stole this syntax from Lua.

import js.Node;
import com.dongxiguo.continuation.Continuation;
using Lambda;
/**
 * @author 杨博
 */

@:build(com.dongxiguo.continuation.Continuation.cpsByMeta("cps"))
class TestNode
{
 
/**
   * Write <code>content</code> to <code>fd</code>.
   */

 
@cps static function writeAll(fd:Int, content:String):Null<NodeErr>
 
{
   
var totalWritten = 0;
   
while (totalWritten < content.length)
   
{
     
var err, written =
       
Node.fs.write(
          fd
, content,
          totalWritten
, content.length - totalWritten, null).async();
     
if (err != null)
     
{
       
return err;
     
}
      totalWritten
+= written;
   
}
 
}
 
 
/**
   * Create a directory named "TestNode", and concurrently put 5 files into it.
   */

 
@cps static function startTest():Void
 
{
   
var err = Node.fs.mkdir("TestNode").async();
   
if (err != null)
   
{
      trace
("Node.fs.mkdir failed: " + err);
     
return;
   
}
   
   
// Lambda.iter() forks threads for each element.
   
// Fork 5 threads now!
   
var fileName = ["1.txt", "2.log", "3.txt", "4.ini", "5.conf"].iter().async();
   
   
// Note that some asynchronous function return more than one values!
   
// It's OK in CPS functions, just like Lua.
   
var err, fd = Node.fs.open("TestNode/" + fileName, "w+").async();
   
if (err != null)
   
{
      trace
("Node.fs.open failed: " + err);
     
return;
   
}
   
   
// Invoke another CPS function.
   
var err = writeAll(fd, "Content of " + fileName).async();
   
if (err != null)
   
{
      trace
("Node.fs.write failed: " + err);
     
return;
   
}
   
   
var err = Node.fs.close(fd).async();
   
if (err != null)
   
{
      trace
("Node.fs.close failed: " + err);
     
return;
   
}
 
}


 
public static function main()
 
{
    startTest
(
     
function():Void
     
{
        trace
("Test is done!");
     
});
 
}
 
}

Michael Cann

unread,
Aug 14, 2012, 10:19:36 AM8/14/12
to haxe...@googlegroups.com
Wow thats nice stuff! :D




--
Mike Cann
http://www.mikecann.co.uk/

Cauê Waneck

unread,
Aug 14, 2012, 10:29:13 AM8/14/12
to haxe...@googlegroups.com
Really cool!!

2012/8/14 Michael Cann <mike...@gmail.com>

Jason O'Neil

unread,
Aug 14, 2012, 10:57:30 AM8/14/12
to haxe...@googlegroups.com
Very impressed, nice work! 

Look forward to trying it out

Andrew Bradley

unread,
Nov 7, 2012, 11:02:40 PM11/7/12
to haxe...@googlegroups.com
In Node, the first argument to any callback is the error.  If this argument is null then the operation completed successfully.  It's the asynchronous equivalent of throwing an exception.

Have you considered changing your CPS library to provide first-class support for this "throwing" behavior?  Basically, if anything non-null is passed as the first argument to a callback, it's assumed to be an error and "thrown".

For example, the following code is written without any CPS magic:
// Read a file from disk and display it.  Uses a hand-written callback.
function foo() {
 
Node.fs.readFile("aFile.txt", function(err, fileContents) {
   
if(err) {
      trace
("An error occurred while trying to read the file from disk");
     
return;
   
}
    trace
(fileContents);
 
});
}



The CPS library, with my proposed modification, would allow that same code to be written like this:
// Read a file from disk and display it.  Uses CPS magic!
@cps function foo() {
 
try {
   
var fileContents = Node.fs.readFile("aFile.txt").async();
    trace
(fileContents);
 
} catch(err: Dynamic) {
    trace
("An error occurred while trying to read the file from disk");
 
}
}



The second block of code is able to use a try-catch instead of an if(err).

This is the approach taken by Streamline.js, a library that performs the same CPS magic for plain Javascript.  It transforms Javascript code that's written in a synchronous style into code that uses callbacks and runs asynchronously.

https://github.com/Sage/streamlinejs

Apologies for bumping an old thread, but this seemed like the best place to ask.
Reply all
Reply to author
Forward
0 new messages