1 of 36

Haxe, a statically-typed language that compiles to Python (and more)

Andy Li�

PyCon HK 2015

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

2 of 36

2

Haxe Foundation

3 of 36

Overview

  • What is Haxe
  • Haxe vs Python
  • The Haxe Python target

3

4 of 36

4

Haxe

C#

5 of 36

Haxe features - JS/Java-like syntax

5

package pycon.hk;

class HelloWorld {

static function main() {

var target = Sys.args()[0];

var speaker = {

first: "Andy",

last: "Li"

}

trace('${speaker.first} ${speaker.last}: Hello, $target!');

}

}

class pycon_hk_HelloWorld:

__slots__ = ()�

@staticmethod

def main():

target = python_internal_ArrayImpl._get(Sys.args(), 0)

speaker_first = "Andy"

speaker_last = "Li"

print(str((((((("" + ("null" if speaker_first is None else speaker_first)) + " ") + ("null" if speaker_last is None else speaker_last)) + ": Hello, ") + ("null" if target is None else target)) + "!")))

PS: showing only the output of the pycon_hk_HelloWorld class

6 of 36

Haxe features - static typing

source code: Typing.hx [output: Typing.py]

6

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

}

}

7 of 36

Haxe features - OOP

source code: OOP.hx [output: OOP.py]

7

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

}

}

8 of 36

Haxe features - functional programming

source code: Functional.hx [output: Functional.py]

8

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");

}

}

}

9 of 36

What is Haxe?

True :D

  • Haxe is free and open source.
  • Haxe is a programming language that compiles to 9 different targets.
  • Haxe provides a set of small yet enough data structures and APIs.
  • Haxe allows accessing native APIs / libraries.
  • If no target-specific things are used, Haxe code “should” automatically work on all targets.

False…

  • Haxe is a young new language. (appeared in 2005)
  • Haxe is a magical program that converts existing app to different platforms.
  • Haxe allows us to use APIs or libraries from arbitrary targets (e.g. use jQuery in the PHP target).
  • Haxe produced program cannot be faster / better than program written in the target language.

9

10 of 36

Why do we want�cross-platform?

10

11 of 36

11

12 of 36

12

http://forum7.hkgolden.com/view.aspx?type=CA&message=6029341

13 of 36

13

http://www.e-zone.com.hk/channelnews.php?id=6974

14 of 36

code reuse

14

15 of 36

platforms / languages come and gone…

15

16 of 36

test all languages!

with your own app!

16

https://attractivechaos.github.io/plb/

17 of 36

Why would one compile� Haxe to Python?

$ diff haxe python

17

18 of 36

the Python libs are great

  • scientific stuffs
  • data analysis
  • machine learning
  • web frameworks
  • etc.

18

19 of 36

static typing vs dynamic typing

  • static typing catches errors earlier
  • type annotation is one kind of doc
  • static typing leads to better code generation -> better performance

19

20 of 36

functional vs imperative

  • Haxe is designed with both OOP & FP in mind.�Python is mostly imperative / OOP.
  • Haxe features not available in Python:
    • expression-oriented syntax + macros
    • GADT + pattern matching

20

21 of 36

the Haxe Python target

status and how-to

21

22 of 36

Current status

  • the youngest one,�added in Haxe 3.2.0 (released in 2015)
  • main authors on Github: �@frabbit, @Simn, and @nadako
  • supports Python 3 only
  • unit tests on the Haxe language specification are passing on all Windows/Mac/Linux for the Python target
    • there are >6000 assertions

22

23 of 36

using Python lib in Haxe

  • use the `untyped` keyword
    • tell the compiler to shut up:
      • undeclared variables are there,
      • types are correct although it looks like it’s not...
  • use `python.Syntax`
    • python.Syntax.pythonCode("#whatever");
  • use externs
    • tell Haxe about the structure and types
    • Automatically generated externs:�https://github.com/andyli/pyextern

23

24 of 36

the `untyped` keyword

source code: Untyped.hx

output: Untyped.py

24

class Untyped {

static function main():Void {

var l = untyped list("abc");

trace(l); // ['a', 'b', 'c']

}

}

# Generated by Haxe

class Untyped:

__slots__ = ()�

@staticmethod

def main():

l = list("abc")

print(str(l))�

Untyped.main()

25 of 36

python.Syntax

source code: PySyntax.hx

output: PySyntax.py

25

import python.Syntax;

class PySyntax {

static function main():Void {

var string = Syntax.pythonCode('"abc" # type : str');

trace(string);

}

}

# Generated by Haxe

class PySyntax:

__slots__ = ()

@staticmethod

def main():

string = "abc" # type : str

print(str(string))

PySyntax.main()

26 of 36

externs

source code: Extern.hx [output: Extern.py]

26

import python.Tuple;

@:pythonImport("inspect") extern class Inspect {

static public function getdoc(object:Dynamic):String;

static public function getmembers

(object:Dynamic, ?predicate:haxe.Constraints.Function)

:Array<Tuple2<String,Dynamic>>;

static public function signature

(obj:Dynamic, ?follow_wrapped:Bool = true)

:Dynamic;

}

class Extern {

static function main():Void {

var getdocdoc = Inspect.getdoc(Inspect.getdoc);

trace(getdocdoc); // "Get the documentation string for an object..."

}

}

27 of 36

Example: Data analysis of a Haxe usage survey

27

28 of 36

Automatic generating externs

  • https://github.com/andyli/pyextern
    • make use of `inspect` and `docutils`
    • `python3 Main.py numpy,scipy[,...] out`
    • cover all classes and functions
    • use `Dynamic` for most things and “guess” type from docstring

28

29 of 36

Function call with named arguments

A typical function in a Python lib:

29

pandas.read_csv(filepath_or_buffer, sep=', ', dialect=None, compression='infer', doublequote=True,escapechar=None, quotechar='"', quoting=0, skipinitialspace=False, lineterminator=None, header='infer',index_col=None, names=None, prefix=None, skiprows=None, skipfooter=None, skip_footer=0, na_values=None,true_values=None, false_values=None, delimiter=None, converters=None, dtype=None, usecols=None,engine=None, delim_whitespace=False, as_recarray=False, na_filter=True, compact_ints=False,use_unsigned=False, low_memory=True, buffer_lines=None, warn_bad_lines=True, error_bad_lines=True,keep_default_na=True, thousands=None, comment=None, decimal='.', parse_dates=False, keep_date_col=False,dayfirst=False, date_parser=None, memory_map=False, float_precision=None, nrows=None, iterator=False,chunksize=None, verbose=False, encoding=None, squeeze=False, mangle_dupe_cols=True, tupleize_cols=False,infer_datetime_format=False, skip_blank_lines=True)

54 arguments!

30 of 36

Function call with named arguments

  • verbose and ugly...

source code: Kw.hx [output: Kw.py]

30

import python.KwArgs;

class Kw {

static function test(a:Float, b:Float, c:Float):Void {

trace('$a, $b, $c');

}

static function main():Void {

test(1.1, 1.2, 1.3);

var kw:KwArgs<Dynamic> = {a: 2.1, b: 2.2, c: 2.3};

(untyped test)(kw);

(untyped test)(({a: 3.1, b: 3.2, c: 3.3}:KwArgs<Dynamic>));

}

}

31 of 36

Function call with named arguments

let’s add sugar :)

31

using PyHelpers;

...

test.call(c => 4.3, a => 4.1, b => 4.2);

...

var data = Pandas.read_csv.call(

dataPath,

sep => "\t",

parse_dates => [0],

header => 0

);

32 of 36

Future work (my personal wish list)

  • Output type annotations �(python 3.5 / cython)
  • Better extern generation, integrate python/typeshed
  • Release Haxe libs to pypi �(like https://github.com/paulfitz/daff)
  • Re-implement python api in haxe?
  • Python 2 support?

32

33 of 36

Made in Haxe

33

34 of 36

The super active Haxe community

34

image: http://www.silexlabs.org/limeopenfl-for-home-game-consoles/

35 of 36

Interested in Haxe?

35

36 of 36

36

Get in touch!