Thursday, 20 October 2011

Compile time extension generation in haXe.

If you don't know haxe, it's a very flexible, statically typed cross platform language.

I'm currently using it for almost all my jobs.

Recently, I've started using it with nodeJs and needed to create some externs (signatures to use host language objects) for various existing APIs (mongodb for instance) you may find those here:

Doing so; I've used an interesting haXe capability which is to define several variations of a function using some special syntax (for externs classes and types) while keeping your typing right.

for instance:
  class Db {

    @:overload(function () : Cursor {})
    @:overload(function (callBack : Error -> Cursor -> Void) : Void {})
    public function collectionsInfo(collection_name : String, callBack : Error -> Cursor -> Void) : Void;


That helps a lot, however, there's currently (haxe evolves quickly) some drawbacks associated with this usage, namely:

- Completion with these alternatives signatures do not work yet (haXe can provide completion information thanks to its lightning fast compiler which is able to return type information of a file position in realtime, as you type..).
- You can't use some advanced haXe niceties (like extensions methods) as this overloading mechanism is implemented as a fallback mode in the compiler, so it can't use this extra information with other features).

Another issue i had specifically with the Javascript target is the difficulty to grasp common javascript patterns you can find in libraries; javascript being so flexible.
One of those is merging objects functionnalities.

A common case is JQuery extensions.
HaXe provides some definitions of JQuery, which is nice, you get completion of the API and it catch your errors ahead of crash.

But at the same time, you cannot use your beloved JQuery plugins as you cannot extends this definition.

In fact, after some discussion on the mailing list, Juraj came with an interesting idea.

Use the compile time macro to create an extern class defining extensions methods in order to provide some inline proxy to the plugin methods. (I promess I've tried to shrink this sentence).

I've decided to implement it as my first haXe macro.
It took less than two hours, and I did not known anything to the API before, thanks to completion and lightning fast compiler, it was a joy to explore, very rewarding.

Let's see some code..

Here's how one would define a small extension (here three methods):

  import JQueryExtension;
  import js.JQuery;

  class MyJQueryExtension implements Extends< JQuery > {
    @:native("val") public function valGet() : Dynamic;
    @:native("val") public function valSet(x : Dynamic) : JQuery;
    @:native("val") public function valFun(f : Int -> Dynamic -> Dynamic) : JQuery;

one need to extends the extension interface and prepend each function with @:native("")

and here's an usage:
  import js.JQuery;
  using MyJQueryExtension;

  class Main {
    static function main() {
      var jq = new JQuery("#someId");

      jq.valGet(); // generates jq.val();
      jq.valSet("content"); // generates jq.val("content");
      jq.valFun(function (i, v) return v); // jq.val(function (i, v) { return v;});
you can see that now the programmer can code against the newly defined API and will have code completion working.

and still have this generated:

Main.main = function() {
    var jq = new js.JQuery("#someId");
    jq.val ();
    jq.val ("content");
    jq.val (function(i,v) {
      return v;

I hope you'll enjoy it; you may find this exemple on my github repository:

As always, feel free to ask.. and no trolling please!

1 comment:

  1. If you do pick to forgo the jQuery Tutorial, ASP Tutorial, ASP.NET tutorial or CSS and you truly wish to hire somebody to do it for you, you still have to look at certain guidelines to find the best developer for your job. Firstly, just because somebody is in another nation, doesn't mean they aren't going to be good.

    jquery jobs