-
-
Notifications
You must be signed in to change notification settings - Fork 661
What's new in Haxe 4
We're introducing the new syntax for specifying function types, now with clear separation of arguments from the return type and with support for argument names, which is very helpful for code self-documenting and better IDE support.
Example:
// no arguments
() -> Void
// single argument
(name:String) -> Void
// multiple (also, optional) arguments
(name:String, ?age:Int) -> Void
// unnamed arguments
(Int, String) -> Bool
// mixed arguments, why not
(a:Int, ?String) -> Void
Note that the old syntax is still supported, but the new one is preferable.
Haxe now finally has the long-awaited arrow function syntax (a.k.a. short lambdas)! This is useful for code that has a lot of "short-and-sweet" callback functions, especially for the code written in the functional paradigm or the code that has to deal with asynchronous operations.
Example:
// no arguments
() -> trace("Haxe is great!")
// equivalent for `function() trace("Haxe is great!")`
// multiple arguments
(a, b) -> a + b
// equivalent for `function(a, b) return a + b;`
// explicit typing
(a:Float, b:Int) -> a + b
// equivalent for `function(a:Float, b:Int) return a + b;`
@:nullSafety
class Main {
static var a:Array<Int>; // Null safety: Field "a" is not nullable thus should have an initial value.
@:nullSafety(Off)
static var b:Array<Int>;
static function main() {}
}
Or in HXML:
--macro nullSafety('package.to.null.check')
final a = 5
(can't be mutated)
final public function foo() { }
(can't be overridden)
final class Foo {}
final interface Bar {}
(can't be extended)
"人".length == 1
Haxe built-in interpreter was rewritten from scratch and now is known as Eval. It is much faster and robust than the old one, meaning that macro and script execution just got faster!
Besides speed, the eval
target has support for interactive debugging, currently available in our VSCode Haxe extension.
Haxe 4 goes with HashLink - the new high-performance run-time developed specifically for Haxe, featuring bytecode+JIT and C output, simple C interop and access to low-level numeric types and pointers.
Java Virtual Machine (JVM) is added as new Haxe compiler target. Generate JVM byte code directly from Haxe bypassing Java compilation step by adding -D jvm
to your project targeting Java. The compilation time is much faster due to the skipped Java step.
It's now possible to inline function calls and inlineable class instantiations by specifying inline
keyword before call/new expression:
inline f()
inline new Obj()
The for
loop syntax now supports iteration over key+value pairs with a new syntax:
for (key => value in collection) {}
User-defined structures can support this by conforming to the new KeyValueIterable protocol.
var a = <hi/>;
This will be available for macro processing as @:markup "<hi/>"
.
Markup strings are supposed to be parsed with a custom parser.
Simple example to convert inline markup to a string with a macro function:
class Test {
static function main() {
var dom = jsx(<hi/>);
trace(dom);
}
static macro function jsx(expr) {
return switch expr.expr {
case EMeta({name: ":markup"}, {expr: EConst(CString(s))}):
macro $v{"XML MARKUP: " + s};
case _:
throw new haxe.macro.Expr.Error("not an xml literal", expr.pos);
}
}
}
More complete examples of parsing JSX-like syntax:
- in @ncannasse's DOMKit: https://github.com/ncannasse/domkit/blob/master/domkit/MarkupParser.hx
- in @back2dos's tink_hxx: https://github.com/haxetink/tink_hxx/blob/master/src/tink/hxx/Parser.hx
Optional fields in structure types defined in the "full" notation can now also be marked as such with ?
, just like in the short notation. Previously one had to use @:optional
metadata which didn't look good and was error-prone.
{ ?f:Int } // short notation
{ var ?f:Int; } // full notation
It's now possible to enable static extensions (a.k.a. using
) for a type at that type's declaration place. This is particularly useful for adding additional methods for enum
types, for example:
@:using(Outcome.OutcomeTools)
enum Outcome<T> {
Success(value:T);
Failure(error:String);
}
class OutcomeTools {
public static function sure<T>(outcome:Outcome<T>):T {
switch outcome {
case Success(value): return value;
case Failure(error): throw error;
}
}
}
The static extension methods from OutcomeTools
can then be used without adding explicit using Outcome.OutcomeTools;
statement:
class Main {
static function main() {
var outcome = load();
outcome.sure();
}
static function load():Outcome<String> {
return Success("Done!");
}
}
Allowed only for enum values without arguments (related issue).
function foo<T>(option:haxe.ds.Option<T> = None) {}
enum abstract E(T) {}
enum abstract E(Int) {
var A; // 0
var B; // 1
}
enum abstract E(String) {
var A; // "A"
var B; // "B"
}
Abstract types now support the "set" version of @:op(a.b)
operator functions (aka "resolve")
abstract DotAccess<T>(Map<String,T>) {
public function new() {
this = new Map();
}
@:op(a.b) function get(field:String):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);
extern inline function f(...) {}
Use custom abstracts if you need something like that. Still available for extern classes.
A & B
(currently only for structures and type param constraints)
Custom property accessors (using function names), which were deprecated 3.x releases have been removed. Instead, use set
, and get
for property declarations.
Old-style (3.X era) example:
class Test {
@:isVar var myvar(get_var, set_var) : String = "Something";
static function main() {
trace (new Test().get_var());
}
public function new() {}
public function get_var() : String { return myvar; }
public function set_var(s:String) : String { return myvar = s;}
}
Change the property accessor types to (set, get)
, and use the standard getter/setter names composed of the prefix get_
or set_
followed by the property name:
class Test {
@:isVar var myvar(get, set) : String = "Something";
static function main() {
trace (new Test().get_myvar());
}
public function new() {}
public function get_myvar() : String { return myvar; }
public function set_myvar(s:String) : String { return myvar = s; }
}
C<T:(A,B)>
now should be C<T:A&B>
var map:Map<Int, String> = [];
var a:haxe.ds.ReadOnlyArray<Int> = [1, 2, 3];
a.push(4); // haxe.ds.ReadOnlyArray<Int> has no field push
Metadata names now can have dots as identifier separators, allowing for more organized and clean metadata naming.
@:prefix.name function() {...}
In future, we might use that feature to re-organize compiler metadata (discussion).
more features, json-based, currently undocumented, implemented in haxe language server and used in vshaxe
https://github.com/vshaxe/vshaxe/wiki/What%27s-New-in-2.0.0
We updated our externs for Web APIs (js.html
) to the latest definitions, fixing bugs and adding missing APIs.
The @:fakeEnum
meta on extern enum
declaration was removed. In modern Haxe, enum abstract
is a proper way to have enum-like behaviour for values that are not originally defined as Haxe enum.
@:native("SomeClassWithStaticConsts")
@:fakeEnum(String)
extern enum SomeEnum {
A;
B;
}
should be changed to
@:native("SomeClassWithStaticConsts")
extern enum abstract SomeEnum(String) {
var A;
var B;
}
The @:fakeEnum
feature was used mostly in Flash extern, which were reworked to use enum abstract instead.
Learn Haxe: Introduction | Manual | API | Try Haxe | Haxe Library Manager | Code Cookbook
Connect: GitHub | Stack Overflow | Forum | Discord | Gitter | Twitter | Facebook