1 of 50

What’s new

4.0

Aleksandr Kuzmenko. Haxe Summit 2019.

2 of 50

A LONG TIME AGO...

Over two years passed

since Haxe 3.4 release.

Seven patch releases,

five preview releases and

two release candidates later...

3 of 50

ALMOST THERE

4 of 50

THANKS TO COMMUNITY

  • Inline markup
  • Inlining functions at call site
  • Arrow functions

5 of 50

SYNTAX

6 of 50

FUNCTION TYPE SYNTAX

Old syntax

New syntax

/** Accepts id and name */

Int->String->Void;

(id:Int, name:String)->Void

Void->Void;

()->Void

Int->?String->Void

(id:Int, ?name:String)->Void

(Int, ?String)->Void

7 of 50

ARROW FUNCTIONS

Full syntax

New syntax

function(i) return i * 2

i -> i * 2

function(i:Int) return i * 2

(i:Int) -> i * 2

function(i, j) return i * j

(i, j) -> i * j

function(i, j):Int return i * j

(i, j) -> (i * j:Int)

8 of 50

ARROW FUNCTIONS

No special representation in syntax tree

is parsed into

(i, j) -> i * j

function(i, j) return i * j

9 of 50

FINAL CLASSES AND METHODS

Old syntax

New syntax

@:final class Main

final class Main

@:final interface IFace

final interface IFace

@:final function method()

final function method()

10 of 50

OPTIONAL FIELDS IN ANON STRUCTS

Old syntax

New syntax

{

@:optional var name:String;

@:optional var address:String;

}

{

var ?name:String;

var ?address:String;

}

11 of 50

ENUM ABSTRACT

Old syntax

New syntax

@:enum abstract Version(Int) {

var Patch = 1;

var Minor = 2;

var Major = 3;

}

enum abstract Version(Int) {

var Patch = 1;

var Minor = 2;

var Major = 3;

}

12 of 50

EXTERN KEYWORD FOR FIELDS

Old syntax

New syntax

@:extern function method()

extern function method()

13 of 50

TYPE INTERSECTION SYNTAX

Old syntax

New syntax

typedef Type3 = {

>Type1,

>Type2,

var field:String;

}

typedef Type3 = Type1 & Type2 & {

var field:String;

}

class MyClass<T:(Type1,Type2)>

class MyClass<T:Type1 & Type2>

14 of 50

EMPTY MAP LITERALS

var m:Map<Int,String> = [];

acceptMap([]);

var o:{ m:Map<Int,String> };

o = { m:[] };

15 of 50

FEATURES

16 of 50

KEY-VALUE ITERATORS

Looks for

  • function keyValueIterator():KeyValueIterator<K,V>
  • or function hasNext():Bool and function next():{key:K, value:V}

typedef KeyValueIterator<K,V> = Iterator<{key:K, value:V}>;

for(key => value in collection) {

trace(key, value);

}

17 of 50

KEY-VALUE ITERATORS

Built-in:

  • String
  • Map
  • haxe.DynamicAccess

var map = ['hello' => 'world'];

for(key => value in map) {

trace(key, value);

}

var obj:DynamicAccess<String> = {

hello:'world'

}

for(field => value in obj) {

trace(field, value);

}

for(index => charCode in "hi") {

trace(index, charCode);

}

18 of 50

NEW MACRO INTERPRETER

  • Called “eval”
  • Several times faster than the old one
  • Supports interactive debugging \o/
  • Used in
    • macros
    • haxe -main MyApp --interp
    • haxe -x MyApp

19 of 50

UNICODE STRINGS

  • String supports unicode on all targets
    • except Neko
    • UTF8: lua, php, python, eval (macro)
    • UTF16: js, cs, java, flash, hl, cpp
  • String API is consistent inside of BMP

"ё".length; // Always 1

"🤨".length; // 2. Sometimes 1

20 of 50

UNICODE STRINGS

Consistent iterators over unicode code points:

  • haxe.iterators.StringIteratorUnicode
  • haxe.iterators.StringKeyValueIteratorUnicode

Output on all targets:

Test.hx:6: 129306

Test.hx:6: 128578

using haxe.iterators.StringIteratorUnicode;

for(code in "🤚🙂".unicodeIterator()) {

trace(code);

}

21 of 50

UNICODE STRINGS

Nuance

  • lua, php
    • random character access is slow
    • string length is re-calculated on each access
  • eval:
    • random access is optimized for sequential indexes
    • string length is cached

22 of 50

NAMESPACED CONDITIONAL FLAGS

  • Allows dots in conditional compilation flags
  • Parentheses are required
  • “target” namespace is reserved for compiler
    • target.name
    • target.utf16
    • target.sys
    • target.static
    • target.threaded

#if (myApp.facebook)

initFacebookApi();

#end

$ haxe build.hxml -D myApp.facebook

23 of 50

NAMESPACED METADATA

@:myLib.magic function method() {}

24 of 50

haxe.ds.ReadOnlyArray

@:forward(concat, copy, filter, indexOf, iterator,

join, lastIndexOf, map, slice, toString)

abstract ReadOnlyArray<T>(Array<T>) from Array<T> {

25 of 50

FINAL FIELDS AND LOCAL VARS

  • final is not constant
  • Protects an identifier from being re-assigned

final a = [1];

a.push(2); // Ok

a = [1, 2]; // Error: cannot assign to final

26 of 50

FINAL FIELDS AND LOCAL VARS

  • Ensures field initialization

class Test {

final first:String; // Error: not initialized

final second:String;

final third:String = 'hello';

function new() {

second = 'world';

}

}

27 of 50

HASHLINK

  • New target designed specially for Haxe
  • Dual compilation
    • compile to bytecode for faster build times
    • compile to C for the best performance

28 of 50

NULL SAFETY

  • Experimental
  • Opt-in
  • No runtime performance impact
  • Enabled per package / class / field
    • @:nullSafety(mode)
    • --macro nullSafety("my.pack", mode)
  • Modes
    • Strict
    • Loose (default)
    • Off

29 of 50

NULL SAFETY

@:nullSafety

function method(?s:String):String {

s.charAt(0); //Error: cannot access field of a nullable value

var str:String = s; //Error: cannot assign Null<String> to String

return s; //Error: cannot return Null<String> as String

}

30 of 50

NULL SAFETY

@:nullSafety

function method(?s:String):String {

if(s != null) {

s.charAt(0); //Ok!

var str:String = s; //Ok!

return s; //Ok!

}

return "default value";

}

31 of 50

NULL SAFETY

@:nullSafety

function method(?s:String):String {

if(s == null) return 'default value';

s.charAt(0); //Ok!

var str:String = s; //Ok!

return s; //Ok!

}

32 of 50

NULL SAFETY

@:nullSafety(Strict)

function method(o:{field:Null<String>}):Void {

if(o.field != null) {

mutate(o);

o.field.charAt(0); //Error: `o.field` might have been changed

}

}

33 of 50

NULL SAFETY

@:nullSafety(Loose)

function method(o:{field:Null<String>}):Void {

if(o.field != null) {

mutate(o);

o.field.charAt(0); //Ok

}

}

34 of 50

NULL SAFETY

@:nullSafety

class Test {

var first:String; //Error: not initialized

var second:String;

var third:String = 'hello';

function new() {

second = 'world';

}

}

35 of 50

ES6 CLASSES

=>

Activated with a compiler flag: -D js-es=6

class Test {

var field:String = 'hello';

function new() {}

function method() {

trace('Hi!');

}

}

class Test {

constructor() {

this.field = "hello";

}

method() {

console.log("Test.hx:7:","Hi!");

}

}

36 of 50

CALL-SITE INLINING

Provides better control over performance / code size balance

function performanceCritical() {

doThis();

inline bigFatFunction();

doThat();

}

public function readInt16() {

var n = inline readUInt16();

if( n & 0x8000 != 0 ) {

//...

}

37 of 50

AUTO-USING FOR TYPES

@:using(Outcome.Tools)

enum Outcome<T> {

Success(value:T);

Failure(error:String);

}

class Tools {

public static function sure<T>(outcome:Outcome<T>):T {

switch outcome {

case Success(value): return value;

case Failure(error): throw error;

}

}

}

38 of 50

RESOLVED FIELDS FOR ABSTRACTS

abstract DotAccess<T>(Map<String,T>) {

public function new() this = new Map();

@:op(a.b) function get(field:String):Null<T>

return this[field];

@:op(a.b) function set(field:String, value:T):T

return this[field] = value;

}

//...

var d = new DotAccess();

d.hello = 5;

trace(d.hello);

39 of 50

INLINE MARKUP

  • Experimental feature
  • Not treated as XML by the compiler
  • Opening and closing tags are
    • <abc
    • </abc>
  • Must be processed by a macro
  • Compiled to

var dsl = <hello Anything is allowed here </hello>;

var dsl = @:markup "<hello Anything is allowed here </hello>";

40 of 50

INLINE MARKUP

var dom = jsx(<hello><world/></hello>);

trace(dom);

macro static function jsx(expr) {

return switch expr.expr {

//handles @:markup "string literal"

case EMeta({name: ":markup"}, {expr: EConst(CString(s))}):

macro $v{"XML MARKUP: " + s};

//everything else

case _:

throw "Not an xml literal";

}

}

41 of 50

INLINE MARKUP

@:build(Js.build())

class Test {

static function main() {

<js>

var o = { hello:"world" };

for(var f in o) {

console.log(f, o[f]);

}

</js>;

}

}

//Somewhere in Js.build()

switch expr.expr {

case EMeta({name: ":markup"}, {expr: EConst(CString(s))}):

if(s.startsWith('<js>') && s.endsWith('</js>')) {

return macro js.Syntax.code($v{s});

}

42 of 50

ENUM VALUES AS DEFAULT FOR ARGS

Only constructors without arguments

Compiled as follows:

enum Test {

NoArg;

WithArg(i:Int);

}

function method(t:Test = NoArg) {}

function method(t:Test) {

if(t = null) t = NoArg;

}

43 of 50

AUTO-VALUE FOR ENUM ABSTRACTS

enum abstract IntValues(Int) {

var Zero; // 0

var One; // 1

var Thousand = 1000;

var ThousandOne; // 1001

}

44 of 50

AUTO-VALUE FOR ENUM ABSTRACTS

enum abstract StringValues(String) {

var Zero; // "Zero"

var One; // "One"

var Thousand; // "Thousand"

}

45 of 50

WHAT’S NEXT

46 of 50

JVM BYTECODE TARGET

Work In Progress

  • Compilation
    • faster
    • no need for JDK
  • Better performance at runtime
  • Doesn’t have issues with Haxe type parameters being incompatible with Java type parameters
  • Possible interactive debugging

47 of 50

COROUTINES

Planned

  • Generic implementation in the language (no built-in support of async/await or yield)
  • Single-threaded
  • Proposal draft: https://github.com/nadako/haxe-coroutines

48 of 50

MODULE LEVEL FUNCTIONS

Planned

Tools.hx

Main.hx

package;

function sayHello(name:String) {

trace('Hello, $name!');

}

package;

import Tools;

class Main {

static function main() {

sayHello('Haxe');

}

}

49 of 50

ASYNCHRONOUS SYS API

Planned

50 of 50

HAXE IS GREAT

Questions?

alex@stablex.ru

twitter @RealyUniqueName