What’s new
4.0
Aleksandr Kuzmenko. Haxe Summit 2019.
A LONG TIME AGO...
Over two years passed
since Haxe 3.4 release.
Seven patch releases,
five preview releases and
two release candidates later...
ALMOST THERE
THANKS TO COMMUNITY
SYNTAX
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
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)
ARROW FUNCTIONS
No special representation in syntax tree
is parsed into
(i, j) -> i * j
function(i, j) return i * j
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()
OPTIONAL FIELDS IN ANON STRUCTS
Old syntax
New syntax
{
@:optional var name:String;
@:optional var address:String;
}
{
var ?name:String;
var ?address:String;
}
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;
}
EXTERN KEYWORD FOR FIELDS
Old syntax
New syntax
@:extern function method()
extern function method()
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>
EMPTY MAP LITERALS
var m:Map<Int,String> = [];
acceptMap([]);
var o:{ m:Map<Int,String> };
o = { m:[] };
FEATURES
KEY-VALUE ITERATORS
Looks for
typedef KeyValueIterator<K,V> = Iterator<{key:K, value:V}>;
for(key => value in collection) {
trace(key, value);
}
KEY-VALUE ITERATORS
Built-in:
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);
}
NEW MACRO INTERPRETER
UNICODE STRINGS
"ё".length; // Always 1
"🤨".length; // 2. Sometimes 1
UNICODE STRINGS
Consistent iterators over unicode code points:
Output on all targets:
Test.hx:6: 129306
Test.hx:6: 128578
using haxe.iterators.StringIteratorUnicode;
for(code in "🤚🙂".unicodeIterator()) {
trace(code);
}
UNICODE STRINGS
Nuance
NAMESPACED CONDITIONAL FLAGS
#if (myApp.facebook)
initFacebookApi();
#end
$ haxe build.hxml -D myApp.facebook
NAMESPACED METADATA
@:myLib.magic function method() {}
haxe.ds.ReadOnlyArray
@:forward(concat, copy, filter, indexOf, iterator,
join, lastIndexOf, map, slice, toString)
abstract ReadOnlyArray<T>(Array<T>) from Array<T> {
FINAL FIELDS AND LOCAL VARS
final a = [1];
a.push(2); // Ok
a = [1, 2]; // Error: cannot assign to final
FINAL FIELDS AND LOCAL VARS
class Test {
final first:String; // Error: not initialized
final second:String;
final third:String = 'hello';
function new() {
second = 'world';
}
}
HASHLINK
NULL SAFETY
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
}
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";
}
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!
}
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
}
}
NULL SAFETY
@:nullSafety(Loose)
function method(o:{field:Null<String>}):Void {
if(o.field != null) {
mutate(o);
o.field.charAt(0); //Ok
}
}
NULL SAFETY
@:nullSafety
class Test {
var first:String; //Error: not initialized
var second:String;
var third:String = 'hello';
function new() {
second = 'world';
}
}
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!");
}
}
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 ) {
//...
}
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;
}
}
}
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);
INLINE MARKUP
var dsl = <hello Anything is allowed here </hello>;
var dsl = @:markup "<hello Anything is allowed here </hello>";
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";
}
}
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});
}
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;
}
AUTO-VALUE FOR ENUM ABSTRACTS
enum abstract IntValues(Int) {
var Zero; // 0
var One; // 1
var Thousand = 1000;
var ThousandOne; // 1001
}
AUTO-VALUE FOR ENUM ABSTRACTS
enum abstract StringValues(String) {
var Zero; // "Zero"
var One; // "One"
var Thousand; // "Thousand"
}
WHAT’S NEXT
JVM BYTECODE TARGET
Work In Progress
COROUTINES
Planned
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');
}
}
ASYNCHRONOUS SYS API
Planned
HAXE IS GREAT
Questions?
alex@stablex.ru
twitter @RealyUniqueName