1 of 26

Transcompiling�towards the freedom of �programming language and platform choice

HKOSCon 2015

Andy Li

To the extent possible under law, Andy Li has waived all copyright and related or neighboring rights to this presentation slides. This work is published from: Hong Kong.

2 of 26

2

Haxe Foundation

3 of 26

3

API

programming language

SDK

Vendor Lock-in

https://www.flickr.com/photos/mike-p/2426668108/

4 of 26

language lock-in?

  • limited “way of thinking”
    • programming paradigm�e.g. object-oriented vs functional
    • typing system�e.g. static typing vs dynamic typing
    • syntax�e.g. Java vs Python vs Lisp vs ...

4

https://www.flickr.com/photos/thebiblioholic/9989733145/

5 of 26

Platform ⇔ Country

programming language ⇔ human language

SDK / API ⇔ work culture

cross-platform ⇔ global business

5

https://www.flickr.com/photos/dennismueller/2952268664/

6 of 26

6

http://www.gamebase.com.tw/news_old/news_content.php?sno=96286681

Transcompiler

7 of 26

7

8 of 26

8

bytecode

Scala

Clojure

9 of 26

9

native bytecode for mobile

C#

10 of 26

10

Haxe

C#

11 of 26

Transcompiler, yay or nay?

Yay! :D

  • an extra layer of abstraction
  • reduced lock-in
  • better code reuse�e.g. isomorphic app
  • pre/post processing�e.g. optimizations, embed assets, macros

Nay... :(

  • an extra layer of abstraction
  • losing benefit of interpreted languages
  • compilation takes time
  • could be hard to debug
  • may have performance overhead
  • may not play well with native libs/APIs

11

A good transcompiler is the one that maximize the Yays and minimize the Nays.

12 of 26

12

​

TypeScript / CoffeeScript

Java

LLVM

Haxe

front-end languages

1

1

> 8

1

back-end targets

JS

JS (via GWT),� native (via LLVM)

JS, native

9

optimizations

minimal

output is messy and slow

good

good

language / compiler features

modern language, minimal compiler feature

old-school

depends on front-end

modern �and rich

compilation speed

fast

slow

depends on front-end

fast

inter-op with native lib/API

good

okay-ish

okay-ish

pretty good

13 of 26

Haxe features - JS/Java-like syntax

source code: hkoscon/transcompiling/HelloWorld.hx

13

package hkoscon.transcompiling;

class HelloWorld {

static function main() {

function greet(speaker, target = "World") {

trace(speaker.first + " " + speaker.last + ": Hello, " + target + "!");

}

var andy = {

first: "Andy",

last: "Li"

};

greet(andy); //Andy Li: Hello, World!

}

}

14 of 26

Haxe features - JS-like syntax

cmd: haxe -main hkoscon.transcompiling.HelloWorld -js HelloWorld.js

14

(function (console) { "use strict";

var hkoscon_transcompiling_HelloWorld = function() { };

hkoscon_transcompiling_HelloWorld.main = function() {

var greet = function(speaker,target) {

if(target == null) target = "World";

console.log(speaker.first + " " + speaker.last + ": Hello, " + target + "!");

};

var andy = { first : "Andy", last : "Li"};

greet(andy);

};

hkoscon_transcompiling_HelloWorld.main();

})(typeof console != "undefined" ? console : {log:function(){}});

15 of 26

Haxe features - static typing

source code: Typing.hx

15

class Typing {

static function main():Void {

var i = 123; // same as var i:Int = 123;

$type(i); // Int

// i = "123"; // error: String should be Int

var floats = [1.0, 1.1, 1.2];

$type(floats); // Array<Float>

$type(floats[0]); // Float

floats[0] = i;

trace(floats); // [ 123, 1.1, 1.2 ]

// floats[0] = "string"; // error: String should be Float

}

}

16 of 26

Haxe features - OOP

source code: Opts.hx

16

class Point {

public var x:Float;

public var y:Float;

public function new(x:Float, y:Float):Void {

this.x = x;

this.y = y;

}

public function offset(dx:Float = 0, dy:Float = 0):Point {

return new Point(x + dx, y + dy);

}

}

​

class Opts {

static function main():Void {

var p = new Point(0, 0);

var p2 = p.offset(1, 2);

trace(p2.x); //1

}

}

​

17 of 26

Haxe features - OOP

cmd: haxe -main Opts -js Opts.js

17

(function (console) { "use strict";

var Point = function(x,y) {

this.x = x;

this.y = y;

};

Point.prototype = {

offset: function(dx,dy) {

if(dy == null) dy = 0;

if(dx == null) dx = 0;

return new Point(this.x + dx,this.y + dy);

}

};

var Opts = function() { };

Opts.main = function() {

var p = new Point(0,0);

var p2 = p.offset(1,2);

console.log(p2.x);

};

Opts.main();

})(typeof console != "undefined" ? console : {log:function(){}});

18 of 26

Haxe features - inlining

source code: Opts.hx

18

class Point {

public var x:Float;

public var y:Float;

inline public function new(x:Float, y:Float):Void {

this.x = x;

this.y = y;

}

inline public function offset(dx:Float = 0, dy:Float = 0):Point {

return new Point(x + dx, y + dy);

}

}

class Opts {

static function main():Void {

var p = new Point(0, 0);

var p2 = p.offset(1, 2);

trace(p2.x); //1

}

}

19 of 26

Haxe features - inlining

cmd: haxe -main Opts -js Opts.js

19

(function (console) { "use strict";

var Point = function(x,y) {

this.x = x;

this.y = y;

};

Point.prototype = {

offset: function(dx,dy) {

if(dy == null) dy = 0;

if(dx == null) dx = 0;

return new Point(this.x + dx,this.y + dy);

}

};

var Opts = function() { };

Opts.main = function() {

var p_x = 0;

var p_y = 0;

var p2_x = p_x + 1;

var p2_y = p_y + 2;

console.log(p2_x);

};

Opts.main();

})(typeof console != "undefined" ? console : {log:function(){}});

previously

var p = new Point(0,0);

var p2 = p.offset(1,2);

console.log(p2.x);

20 of 26

Haxe features - dead code elimination

cmd: haxe -main Opts -js Opts.js -dce full

20

(function (console) { "use strict";

var Opts = function() { };

Opts.main = function() {

var p_x = 0;

var p_y = 0;

var p2_x = p_x + 1;

var p2_y = p_y + 2;

console.log(p2_x);

};

Opts.main();

})(typeof console != "undefined" ? console : {log:function(){}});

21 of 26

Haxe features - static analysis

cmd: haxe -main Opts -js Opts.js -dce full -D analyzer

21

(function (console) { "use strict";

var Opts = function() { };

Opts.main = function() {

var p_x = 0;

var p2_x = p_x + 1;

console.log(p2_x);

};

Opts.main();

})(typeof console != "undefined" ? console : {log:function(){}});

22 of 26

Haxe features - functional programming

22

using Lambda; // static extension

import haxe.ds.*;

class Functional {

static function main() {

// Array comprehension

var evens:Array<Float> = [for (i in 0...15) if (i % 2 == 0) i];

trace(evens); // [ 0, 2, 4, 6, 8, 10, 12, 14 ]

// functional goodies from `using Lambda`

var maxMultipleOf4 = evens

.filter(function(i) return i % 4 == 0)

.fold(function(i, a) return Math.max(i, a), evens[0]);

trace(maxMultipleOf4); // 12

// enum (GADT) and pattern matching

function getAnyHigher(floats:Array<Float>, v:Float):Option<Float> {

for (f in floats)

if (f > v)

return Some(f);

return None;

}

switch (getAnyHigher(evens, 5)) {

case Some(value):

// string interpolation (not really FP, but still nice)

trace('In evens, $value is higher than 5');

case None:

trace("No value in evens is higher than 5");

}

}

}

23 of 26

Haxe features - compile-time macros

output:

23

class Versioned {

macro static function getGitVersion():haxe.macro.Expr {

var git = new sys.io.Process("git", ["describe", "--tags"]);

if (git.exitCode() != 0) {

throw "`git describe --tags` failed: " + git.stderr.readAll().toString();

}

var tag = git.stdout.readLine();

return macro $v{tag};

}

public static var VERSION(default, never):String = getGitVersion();

static function main() {

trace(VERSION); // something like "1.0-1-g123b31f"

}

}

(function (console) { "use strict";

var Versioned = function() { };

Versioned.main = function() {

console.log(Versioned.VERSION);

};

Versioned.VERSION = "1.0-1-g123b31f";

Versioned.main();

})(typeof console != "undefined" ? console : {log:function(){}});

24 of 26

Summary

Haxe

  • 9 compilation targets
    • reduced language/platform lock-in
  • modern language and compiler features
    • justified the extra compilation step
  • a fast optimizing compiler
    • minimized compilation time and performance overhead

24

25 of 26

Interested in Haxe?

  • Read => http://haxe.org/
  • Try => http://try.haxe.org/
  • Get =>
    • with an installer: http://haxe.org/download/, or
    • with a package manager:
      • `brew install haxe` on Mac
      • `choco install haxe` on Windows
      • use a ppa on Ubuntu�(the one from apt-get is super outdated...)

25

26 of 26

26

Get in touch!