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
Haxe Foundation
3
API
programming language
SDK
Vendor Lock-in
https://www.flickr.com/photos/mike-p/2426668108/
language lock-in?
4
https://www.flickr.com/photos/thebiblioholic/9989733145/
Platform ⇔ Country
programming language ⇔ human language
SDK / API ⇔ work culture
cross-platform ⇔ global business
5
https://www.flickr.com/photos/dennismueller/2952268664/
6
http://www.gamebase.com.tw/news_old/news_content.php?sno=96286681
Transcompiler
8
bytecode
Scala
Clojure
9
native bytecode for mobile
C#
10
Haxe
C#
Transcompiler, yay or nay?
Yay! :D
Nay... :(
11
A good transcompiler is the one that maximize the Yays and minimize the Nays.
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 |
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!
}
}
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(){}});
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
}
}
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
}
}
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(){}});
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
}
}
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);
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(){}});
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(){}});
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");
}
}
}
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(){}});
Summary
Haxe
24
Interested in Haxe?
25
26
Get in touch!