Haxeの次期大型アップデート Haxe 4.0 は近日リリース予定です。
この記事の執筆時点の最新開発バージョンは 4.0.0-rc.2 です。2019年5月8日にHaxeの大規模カンファレンスが開催されるため、これに合わせて 4.0.0-final がリリースされる見込みでした。しかし、一部機能の開発が収束していないため、 直近のカンファレンスでは 4.0.0-rc.3 のリリースに留まる可能性も出てきています。
とはいえ、Haxe 4.0は現状のバージョンで既に実戦投入レベルには達しています。私の周辺では、既に3プロジェクトがHaxe 4.0に移行済みで、うち1つが本番稼働中、他2つも数か月以内にはリリースを予定しています。
この記事では、 JavaScriptターゲットに関するものに絞った Haxe 4.0 の変更点 をまとめました。Haxeは JavaScript, C++, C#, PHP, Flash, … などマルチターゲットにコンパイルできる言語ですが、全ターゲットの情報を追うのはしんどいので、JavaScript以外のターゲットについては、皆さんの方で調べてみてください。
コンパイラサービスによる入力補完が大きく改善され、単純なコード補完だけではなく、auto-importや文脈に従ったコード生成もできるようになりました。
また、Haxe本体の開発と並行して、開発環境の整備も行われています。Haxeコミュニティ公式は VS Code の対応を重点的に進めていますが、LSP(Language Server Protocol)を実装した Haxe Language Server も同時に提供されています。LSPに対応しているエディタであれば、Haxe開発環境を構築することが容易になりました。
以前から要望の強かったアロー関数構文が追加されました。次のように function と return を省略することができます。
// Haxe 3.x
array.filter(function (x) return x % 2 == 0).map(function (x) return x * 10);
// Haxe 4.0
array.filter(x -> x % 2 == 0).map(x -> x * 10);構文は次の通りです。
// 引数なし
() -> expr
// 単一引数
a -> expr
(a) -> expr
(a: Int) -> expr
// 複数引数
(a, b) -> expr
(a: Int, b: String) -> exprreturn を明示的に記述することも可能です。関数の途中で return して、末尾で return しなかった場合は、末尾の式が戻り値として扱われます。
(a: Int) -> {
if (a < 0) return false;
true; // return true; と等価
}値を指定しない return を記述すると、戻り値が Void のアロー関数となります。
// Void -> Void
() -> return次の例のように、引数名のついた形式で関数型を宣言できるようになりました。これにより、単純に型の自己文書化が高まる他、コンパイラサービスがこの情報を元にAPIヒントを表示したりコード生成したりするようにもなりました。
// Haxe 3.xまでの構文(Haxe 4.0以降も引き続き使用可能です)
String -> Int -> Void
// Haxe 4.0で追加された構文
(name: String, age: Int) -> VoidHaxeコミュニティ公式としては、新しい構文の使用を推奨しています。
var x 構文の代わりに final x 構文で変数を宣言すると、イミュータブル変数が定義できるようになりました。Haxe 3.xまではinlineやリードオンリーなプロパティで部分的には代用はできていましたが、この構文により、ローカルレベルの変数もイミュータブルにできるようになりました。
final 変数は原則として宣言時のみ代入(初期化)が可能ですが、static ではないフィールドレベルのイミュータブル変数に限り、コンストラクタでの代入も可能です。
class Foo {
static final foo = 10;
final bar = 100;
final baz: String;
public function new(baz: String) {
this.baz = baz;
}
public function toString(): String {
final val = foo + bar;
return `${baz}:${val}`;
}
}Haxe 3.x以前からの古い構文 var x(default, never) で定義したプロパティと、 final x 構文で定義したフィールドは同じものとして扱われます。ただし、古い構文はHaxe 4.1以降で非推奨となる(もしくは削除される)可能性があるため、Haxe 4.0以降は原則として final を使うことが推奨されます。
また、Haxe 4.0のコンパイラは、次の記述をどちらも同じものとして扱います。
static inline var x = 10;
static inline final x = 10;Map などの key-value を持つデータに対し、keyとvalueを同時に取得できるイテレーター構文 for (key ⇒ value in e) が追加されました。
var map = [ "foo" => 10, "bar" => 20 ];
for (key => value in map) {
trace(key, value);
}この構文は、構造的部分型 KeyValueIterator<K, V> を満たしているオブジェクトに対して使用可能です。上記の例では、 Map<K, V> が KeyValueIterator<K, V> を満たしています。
for に対してIntIterator 0…5 や配列リテラル [1, 2, 3] などのコンパイル時点でループ回数が判定できる値を指定した場合、ループ展開(loop-unrolling)されるようになりました。
// Haxe
for (i in 0...5) {
trace(i);
}// JavaScript
console.log("Main.hx:4:",0);
console.log("Main.hx:4:",1);
console.log("Main.hx:4:",2);
console.log("Main.hx:4:",3);
console.log("Main.hx:4:",4);あくまでも実験的機能ですが、 null-safety(null安全性チェック)機能が追加されました。
コンパイラフラグで --macro nullSafety("my.pack") を対象とするパッケージを指定する方法か、classに対して @nullSafety メタデータを指定する方法のどちらかで、この機能を有効にできます。nullチェックはクラスと抽象型を対象にできます。(注:4.0.0-rc.2の時点ではクラスのみにしかnullチェックが行われていませんでしたが、最新開発バージョンでは抽象型に対してもnullチェックが行われるように改善されています。)
この機能を有効にしている場合、明示的に Null<T> 型として定義されている箇所以外では、 null を代入することが不可能になり、コンパイルエラーが出力されるようになります。
var a: Null<Int> = null; // no error
var b = null; // Null safety: Cannot assign nullable value here.Null<T> 型の値を参照する時は、nullチェックが必須になります。
function exsample(foo: Null<String>, bar: Null<String>): Void {
// no error
if (foo != null) {
var f = foo;
}
// Null safety: Cannot assign nullable value here.
var b = bar;
}また、--macro nullSafety("my.pack", Loose) や @nullSafety(Off) のように、Strict, Loose, Off の3段階のチェックレベルを指定できます。省略時の既定値は Loose です。 Off を指定するとnullチェックは行われません。
Strict と Loose の差異は、次のようなコードで現れます。この例では、nullチェックのifの内側で mutate() を呼び出していますが、mutate() により null が代入されています。
function example(o: {field: Null<String>}) {
if (o.field != null) {
mutate(o);
var notNullable: String = o.field; // Strictではエラー、Looseではエラーとならない
}
}
function mutate(o: {field: Null<String>}) {
o.field = null;
}var x = null; if ("foo" == x) { } のような比較が最適化され、効率的に処理されるようになりました。
@:using メタデータにより型デコレーションができるようになりました。 Static Extension と類似する機能です。
// Maybe.hx
@:using(MaybeTools)
enum Maybe<T> {
Just(x: T);
Nothing;
}// MaybeTools.hx
class MaybeTools {
public static function isJust<T>(a: Maybe<T>): Bool {
return switch (a) {
case Just(_): true;
case Nothing: false;
}
}
}final x = Maybe.Nothing;
trace(x.isJust()); // falseHaxe 4.0.0-rc.2の時点では、classとenumに対してのみ指定可能ですが、4.0.0-finalではinterfaceとabstractに対しても指定可能になりそうです。
イミュータブルフィールドを定義する final x と、オプショナルフィールドを定義する var ?x final ?x 構文が追加されました。
{
final a: Int;
var ?x: String;
final ?y: Bool;
}var x(default, never) 構文はイミュータブル変数と同様に、 final x 同義です。Haxe 4.1以降で非推奨となる(もしくは削除される)可能性があります。
var ?x final ?x は、それぞれHaxe 3.xと同様に @:optional メタデータを使って @:optional var x @:optional var x 記述することも可能ですが、将来的には @:optional メタデータは非推奨となるものと思われます(注:私の知る限りでは非推奨とするという情報は見つけられていません)。
交差型とは、複数の匿名構造体型を1つの型に合成したものです。 交差型を定義する A & B 構文が追加されました。
Haxe 2.xから匿名構造体型には「継承」というほぼ同じ概念がありますが、交差型はこれを構文レベルで置き換えます。交差型構文の導入により、Haxe 4.0からは交差型構文が推奨とアナウンスされていますが、古い「継承」構文でも引き続き記述できます。
typedef Type1 = { var name: String; }
typedef Type2 = { var age: Int; }
// Haxe 4.0:交差型構文
typedef Type3 = Type1 & Type2;
typedef Type4 = Type1 & Type2 & {
function toString(): String;
}
// Haxe 3.x:「継承」構文
typedef Type3 = {>Type1, >Type2, };
typedef Type4 = {
>Type1,
>Type2,
function toString(): String;
}また、型パラメーター制約の構文が交差型の構文と統一されました。複数の匿名構造体型を型パラメーター制約として指定する場合、 T: A & B のように記述します。旧構文 T: (A, B) はHaxe 4.0で削除されたため、Haxe 3.xから移行する際は修正が必要となります。
// Haxe 4.0
class Foo<T: Type1 & Type2> { }
// Haxe 3.x:この構文はHaxe 4.0以降では使えません
class Foo<T: (Type1, Type2)> { }enum abstract 型を定義する際、 @:enum メタデータではなく、 enum 修飾子を使って記述できるようなりました。Haxe 4.0時点では enum 修飾子と @:enum メタデータは共に使用可能ですが、将来的に @:enum メタデータは非推奨になるものと思われます(注:私の知る限りでは非推奨とするという情報は見つけられていません)。
// Haxe 4.0
enum abstract HttpStatus(Int) {
var NotFound = 404;
var MethodNotAllowed = 405;
}
// Haxe 3.x
@:enum abstract HttpStatus(Int) {
var NotFound = 404;
var MethodNotAllowed = 405;
}基底型がInt型かString型の enum abstract 型に限り、値を省略して定義することが可能になりました。この場合、次のように自動的に値が設定されます。
enum abstract Enum1(Int) {
var A; // 0
var B; // 1
var C = 1000; // 1000
var D; // 1001
var E; // 1002
}
enum abstract Enum2(String) {
var A; // "A"
var B; // "B"
var Hello; //"Hello"
}@:fakeEnum メタデータはenum abstractとほぼ同等機能を提供するレガシー機能です。レガシー扱いとなってからも長い間残されていましたが、Haxe 4.0でついに削除となりました。
パラメータを持たないenum値に限りますが、enum値をデフォルト引数に指定できるようになりました。
function foo(option: haxe.ds.Option<Int> = None) { /* ... */ }クラスとインターフェースの継承禁止、メソッドのオーバーライド禁止の指定方法が、 @:final メタデータではなく、 final 修飾子を使う構文に変更なりました。Haxe 4.0時点では final 修飾子と @:final メタデータは共に使用可能ですが、将来的に @:final メタデータは非推奨になるものと思われます(注:私の知る限りでは非推奨とするという情報は見つけられていません)。
// Haxe 4.0
final class Foo { /* ... */ }
final interface Bar { /* ... */ }
class Baz {
public final function hello(): String {
return "hello";
}
}
// Haxe 3.x
@:final class Foo { /* ... */ }
@:final interface Bar { /* ... */ }
class Baz {
@:final public function hello(): String {
return "hello";
}
}switch 式で変数キャプチャを使う場合、 case に var final を記述できるようになりました。 final を付けた場合、キャプチャした変数はイミュータブルとなります。
Haxe 3.xまでと同様に var final を記述せずに省略しても構いません。省略した場合は var と同等として扱われ、キャプチャした変数はミュータブルとなります。
final array = [1, 2, 3];
switch (array) {
case []: trace("empty");
case [a]: trace(a);
case [var a, final b]: trace(a, b);
case final x: trace(x);
}フィールドレベルに対してのexternの指定が @:extern メタデータではなく、 @: が不要な extern 修飾子を使って記述できるようなりました。Haxe 3.xではclassに対してのみ extern 修飾子が指定できましたが、この変更により構文が統一されました。
Haxe 4.0時点では extern 修飾子を使って記述したコードと @:extern メタデータを使って記述したコードの扱いは同じですが、将来的にメタデータを使った記述方法は非推奨になるものと思われます(注:私の知る限りでは非推奨とするという情報は見つけられていません)。
. 演算子オーバーロードにgetterだけでなくsetterも定義できるようになり、代入を記述することが可能になりました。
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);inline 修飾子が付いていない関数・メソッドも、呼び出し側で inline を指定して呼び出すことで、強制的にインライン展開できるようになりました。コンストラクタに対しても指定可能ですが、プロパティやフィールドに対しては指定できません。
final p1 = inline new Point(0, 1);
final str = inline p1.toString();
trace(str);
final p2 = inline Point.fromArray([0, 1]);class Point {
public final x: Int;
public final y: Int;
public function new(x: Int, y: Int) {
this.x = x;
this.y = y;
}
public function toString(): String {
return '[$x, $y]';
}
public static function fromArray(p: Array<Int>): Point {
return new Point(p[0], p[1]);
}
}型安全性が壊れるという理由で、 Implementing Dynamic を使用できるのは extern class のみに制限されました。
Haxe 3.xの時点で非推奨扱いになっていた古い構文が削除されました。
C++ターゲットでは動作していたようですが、他のターゲットでは動作しないため、構文自体が削除となりました。
次のようなコードがHaxe 3.xでは動作していましたが、Haxe 4.0から動作しなくなりました。
using StringTools;
class Test {
static function main() {
var myInt: MyInt = 10;
var str = myInt.rpad("_", 10); //暗黙的にtoString()してStringTools.rpad()
trace(str);
}
}
abstract MyInt(Int) from Int {
@:to
public function toString(): String {
return Std.string(this);
}
}using StringTools;
class Main {
static function main() {
new MyString();
}
}
abstract MyString(String) {
public function new() {
this = "123";
trace(this.rpad("#", 10)); //OK: これはHaxe4.0でも動作します
trace(rpad("#", 10)); //NG
}
}static var x: String; //OK
static var y = 1; //OK
static var z; //NGメタデータ名に . が使えるようになりました。 @:js.someName のように記述できます。
コンパイラフラグに -D warn-var-shadowing が追加されました。有効にすると、変数シャドーイングをしている個所に対して、コンパイラ警告が出力されます。
class Main {
static function main() {
var a = 1;
var a = 2;
trace(a);
}
}Main.hx:4: characters 13-14 : Warning : This variable shadows a previously declared variable Main.hx:3: characters 13-14 : Warning : Previous variable was here
次のパッケージが標準ライブラリから削除され、 hx3compat に移されました。
-
js.jquery-
元々、APIサポート状況が悪く haxelib の jQueryExtern を使うべきという状況でした。
-
時代の流れで、jQueryの重要度が下がっているという背景もあるかと思われます。
-
-
js.swfobject-
SWFObjectを操作するAPIですが、流石に時代の流れを感じますね。
-
-
haxe.unit-
Unit Testの実装ですが、実用には厳しい非常に素朴なAPIしか提供されておらず、実利用されていなかったと思われます。
-
少し前に HaxeのUnitTestライブラリ(2018年版) という記事を書いたので、代替手段はこちらを参考にしてください。
-
-
haxe.remoting-
HaxeでFlash/AMP RemotingやJS/Flash間の通信をサポートするパッケージですが、これも今更感があり、時代の流れですね。
-
-
haxe.web-
HaxeでHTTP APサーバーを構築するための簡易的なRouterの実装です。
-
おそらくNekoターゲットを想定して作られたものと思われ、ほとんど利用されていなかったと思われます。
-
Haxeは長らくUnicode対応が不完全で、標準ライブラリのみでUnicodeを処理してはならないという残念な状況が続いていましたが、Haxe 4.0でやっと改善が行われることになりました。公式ブログではパフォーマンス上の理由や歴史的経緯が説明されていますが、個人的には単にemojiの利用が広まって逃げられなくなっただけではと認識しています。
主な改善点は次の3点です。
-
haxe.Utf8がサロゲートペアを考慮した実装に変更 -
haxe.io.BytesのofString()などでエンコーディングを指定可能に -
Stringにコードポイントベースのイテレーターメソッドiterator()、keyValueIterator()が追加
JavaScriptターゲットとは直接関係はしませんが、sys パッケージもファイルパスなどを扱うためにUnicode対応が進められています。
注意点として、Haxe 4.0以降も String は引き続きプラットフォーム依存で、原則としてネイティブAPIが呼び出されるだけとなります。JavaScriptターゲットでの "🐐".length の結果は 2 となりますが、Evalターゲットでの結果は 1 となります。私が確認した限り、StringTools や StringBuf などは String.length で処理を行っています。
Haxe公式ブログでも、Haxe 4.0がUnicode対応の最初の一歩で、4.1でも継続して対応を行うと言っているので、IssueやPRを出していけば以前よりも積極的に対応されると思われます。
Std.is(x, T) は x == null の場合、 T にどんな型を指定しても必ず false を返すように規定されました。
catch (e: T) は null がスローされてきた場合、必ず型アノテーションが Dynamic と指定されている catch でのみキャッチされるように規定されました。
cast(x, T) はJavaScriptなどの静的ではない環境で null を セーフキャスト cast(x, T) した場合は、必ず null を返すようになりました。なお、C++などの静的環境では、 cast(null, Int) は 0、cast(null, Bool) は false になるなど、環境差異が生じることに注意が必要です。
abstract enumに対して Type.enumEq() を使う事ができなくなりました。
-
haxe.iteratorsパケージの追加 -
MapにkeyValueIterator()を追加 -
haxe.DynamicAccessにiterator()、keyValueIterator()を追加
Lambda クラスの map() や concat() などの関数(スタティックメソッド)の戻り値が List から Array に変更されました。
haxe.xml.Access という Xml classをベースとしたAPI互換のあるabstract型が作成され、haxe.xml.Fast はそれに対するaliasに変更されました。
trace() 内部のログフォーマット処理が haxe.Log.formatOutput() に切り出されました。
Haxeでは trace() の実体 haxe.Log.trace() が dynamic function として定義されており、この関数を上書きすると trace() の挙動を自由に変更できます。 trace() のメッセージフォーマットは変更しなくてもよいが、メッセージ出力先を変更したいケースが haxe.Log.formatOutput() の典型的な使用例となります。
Object、Date、Promise、Error などのJavaScript Nativeのビルトインオブジェクトが js.lib パッケージ配下に統合されます。
js.Promise などのHaxe 4.0より前から存在していた整備対象のモジュールは、互換性維持のため、 js.lib パッケージへのエイリアスとして従前のパッケージ配下にも残されます。ただし、このエイリアスは @:deprecated メタデータが付与され、使用するとコンパイル警告が出力されます。
-
js.lib.Object -
js.lib.Error←js.Error-
このモジュール内に、
EvalError,RangeError,ReferenceError,SyntaxError,TypeError,TypeErrorも定義されています。
-
-
js.lib.Function -
js.lib.Date -
js.lib.RegExp←js.Promise -
js.lib.Promise←js.RegExp -
js.lib.Map -
js.lib.Set -
js.lib.Iterator -
js.lib.Symbol -
js.lib.ArrayBuffer←js.html.ArrayBuffer -
js.lib.ArrayBufferView←js.html.ArrayBufferView -
js.lib.DataView←js.html.DataView -
js.lib.Float32Array←js.html.Float32Array -
js.lib.Float64Array←js.html.Float64Array -
js.lib.Int16Array←js.html.Int16Array -
js.lib.Int32Array←js.html.Int32Array -
js.lib.Int8Array←js.html.Int8Array -
js.lib.Uint16Array←js.html.Uint16Array -
js.lib.Uint32Array←js.html.Uint32Array -
js.lib.Uint8Array←js.html.Uint8Array -
js.lib.Uint8ClampedArray←js.html.Uint8ClampedArray
HTML/DOM関連のJavaScript Web API(ブラウザAPI)が最近の状況に合わせて更新されています。ざっと確認した限りでは、getUserMedia() ServiceWorker Push API WebMIDI などが入っているようです。
Haxeコードから生のJavaScriptコードを注入する js.Syntax が追加されました。蛇足的ですが、同様に php.Syntax と python.Syntax も追加されています。
以前からほぼ同等機能の untyped 関数が提供されていましたが、js.Syntax はコンパイラ・コードアナライザーに対してやさしくなっていることが大きな違いです。 untyped ではまったくコンパイラの支援が受けられませんが、js.Syntax はある程度のコンパイラ支援が受けられます。
Haxe 4.0では untyped 関数が非推奨となったため、js.Syntax を使うようにしてください。
Haxe/JSではどのような値も例外としてスローできます。throw 1 のように js.Error 派生classではない型の値をスローするコードをコンパイルすると、Haxeコンパイラは throw new js$Boot_HaxeError(1) のように、例外用の型にラップしてからスローするJavaScriptコードを出力します。また、 try catch でスローされてきた例外の値を参照するHaxeコードは、 e instanceof js$Boot_HaxeError でラップされた値かを判定してアンラップするJavaScriptコードとして出力されます。
この挙動を気にすることは滅多にないのですが、js__$Boot_HaxeError がスローされてこないことが明白な場合(JavaScript Native側で発生する例外をキャッチする場合など)にアンラップ処理を行いたくないことは稀に発生します、そのような場合に js.Lib.getOriginalException() を使うと、アンラップ処理を行っていない例外の値を取得できます。
Haxe 3.xまでの型定義では then() が正しく型推論できないことが度々発生していましたが、この問題が修正されました。
コンパイラフラグ -D js-es が 6 も受け付けるようになりました。-D js-es=6 を指定すると、ES6class構文を使ったJavaScriptコードが出力されます。
コンパイラフラグで -D source-map を指定すると、 -debug 指定なしのリリースビルドでもSourceMapを出力するようになりました。
なお、同様のオプション -D js-source-map と PHP用の -D source_map がHaxe4.0より前のバージョンから存在しているのですが、こちらも引き続き使用可能です。
Haxe 3.xでは Array#iterator() でIteratorに変換すると効率の悪いJavaScriptコードが生成されていましたが、Haxe 4.0で改善されました。 当然Iteratorに変換すると若干冗長なJavaScriptコードになるのですが、今時のJITが搭載されたJavaScript実行環境であれば実行速度に有意差が出ることはまずないはずです。
enumの実行時の内部表現が配列からオブジェクトに変更され、性能が改善しました。もしenumの内部表現を配列にしたい場合は、 コンパイラフラグ -D js-enums-as-arrays を指定します。
Haxe/JSでは、 実行時に Std.is() でインターフェース実装を判定するために、JavaScriptコード生成時に obj.interfaces メタデータを付与しています。この実行時メタデータをインターフェースを実装していて、かつ Std.is() が使われているクラスのインスタンスに対してのみ生成するように改善されました。
また、 Std.is() が obj.interfaces を参照する回数が最小限となるように修正され、性能が改善しました。
Std.is() でクラスと比較を行うHaxeコードから、 instanceof を直接使うJavaScriptコード出力されるように修正され、性能が改善しました。
Haxe 3.xでは、Promise#catchError() をコンパイルすると promise["catch"]() のようなブラケット記法のJavaScriptコードが出力されることが度々ありましたが、promise.catch() のようなドット記法のJavaScriptコードが出力されるように改善されました。
次のような「ループ内の switch で break するコード」をコンパイルすると、Haxe 3.xでは throw で break を実現するJavaScriptコードが出力されていました。このため、ブラウザの開発者コンソールではthrowでデバッガが反応して停止してしまう問題がありましたが、throw を使わないように改善されました。
// Haxe
for (i in 0...10) {
switch (i) {
case 0: trace(i);
case 1: break;
case _:
}
}// JS that is compiled by Haxe 3.4.7
var _g = 0;
try {
while(_g < 10) {
var i = _g++;
switch(i) {
case 0:
console.log(i);
break;
case 1:
throw "__break__"; //これが問題だった
break;
default:
}
}
} catch( e ) { if( e != "__break__" ) throw e; }コンパイル時マクロのインタプリタが、NekoVM neko から eval に置き換えられました。Haxeコンパイラのインタプリタ実行機能(--interp と -x) も同様に eval で実行されるように変更されました。インタプリタとは言っていますが、JITコンパイラが搭載されており、高速に動作します。
Haxeはコンパイルが非常に速い事が特徴の一つですが、Haxe 3.xまではマクロの実行だけは遅いという問題がありました。NekoVM自体はそれほど遅いわけではないのですが、マクロ実行に最適化されているevalに置き換えたことで速度が大幅に改善されています。
eval への変更に合わせ、一部のマクロで修正が必要になります。Haxe 3.xまではマクロが neko で実行されていたため neko ターゲット向けのコードを呼び出すことが可能でしたが、Haxe 4.0のマクロでは eval で実行されるため neko ターゲットのコードは呼び出すことができません。そのため、当該箇所については neko から eval への移行が必要となります。
また、eval の開発用に、コンパイラフラグ -D eval-stack -D eval-times -D eval-debugger が追加されました。詳細は次のURLを参照してください。
XMLライクなマークアップ文字列を直接Haxeコード内に記述できるようになりました。ただし、この構文はマクロでしか使用できません。マクロでインラインマークアップを処理する際、 @:markup "<tag />" と等価のASTとして扱われます。
class Main {
static function main() {
final markup = <div>
<p>hello</p>
</div>;
final dom = xml(markup);
trace(dom);
}
static macro function xml(expr) {
return switch (expr.expr) {
case EMeta({name: ":markup"}, {expr: EConst(CString(s))}):
macro $v{"XML: " + s};
case _:
throw new haxe.macro.Expr.Error("マークアップ構文ではありません", expr.pos);
}
}
}この構文は、Haxeのスポンサー企業(Massive Interactive)および中心的な開発者が Haxe/JS + React.js で開発を行っており、シームレスにJSXを使いたいというモチベーションから導入に至ったようです(一応、公式のプロポーザルでは、JSX以外にもXMLベースの定義情報は沢山あるから欲しいよねという書き方がされています)。
Null<T> が typedef Null<T> = T から @:coreType abstract に変更されました。
以前から Null<T> 型は特別扱い をされていたため、普通にHaxeコードを書く分には特に違いを感じる部分はないはずですが、ASTの型が変更されたため、マクロに影響があります。
マクロ内で定義されたstatic変数は、コンパイルサーバーであっても必ずコンパイル毎に初期化するように変更されました。もしこの挙動に不都合がある場合は、static変数に対して @:persistent メタデータを付与すると、コンパイル毎の初期化は抑制されます。
前述のマクロでのstatic変数の扱いが変更されたことにより、存在意義を失ったAPIが非推奨となりました。
Haxe 3.xでマクロからドキュメントコメントの参照するには、コンパイラオプションで -D use-rtti-doc の指定が必要でした。Haxe 4.0のコンパイルサーバーはドキュメントコメントを常に保持するようになったため、このオプションは存在意義を失い、削除されました。
-D a.b のように . を含む値もコンパイラフラグとして受け付けるようになりました。また、 . を含むコンパイラフラグを条件付きコンパイルに指定する場合は、 #if (a.b) のように記述します。
自動付与されるコンパイラフラグ target.name、target.static、target.sys、target.utf16、target.threaded が追加され、コンパイル時に参照できるコンパイルターゲット情報が詳細化されました。
自動付与されるコンパイラフラグ haxe4 が追加されました。なお、Haxe 4.0でも haxe3 フラグが依然として有効となっていることに注意が必要です。
今までは、neko.vm.Thread や java.vm.Thread など、各ターゲットごとにThread APIが定義されていましたが、 sys.thread パッケージ配下に統合されました。これは本来的には標準ライブラリの話題ですが、JavaScriptターゲットでは元々Threadはサポートされておらず、使いどころはマクロのみとなるため、この記事ではマクロの話題として扱っています。
標準ライブラリでUnicode対応の強化が進められていますが、Haxeコンパイラ自体のUnicode対応も行われました。
Haxeコンパイラの字句解析器がUTF-8で処理を行うように変更されました。Haxe 4.0では、この変更を足掛かりとして、入力補完サービスなどのコンパイラの機能改善が図られています。
入力補完以外の目に見える変更点としては、コンパイラ内部で扱うカラム位置がバイトオフセット(0オリジン)ではなく文字オフセット(1オリジン)に変更されました。この変更により、コンパイラサービス(入力補完やコンパイルエラー等)が指すコード位置が、文字オフセットベースに変わりました。
また、コンパイラフラグに -D old-error-format が追加されました。このオプションを有効にすると、Haxe 3.xまでと同様の動作をしますが、高度な入力補完機能が動作しなくなってしまうため、余程のことがない限りは使う事はないものと思います。
いくつかのパターンのコンパイルエラーが分かりやすいメッセージに改善されました。
-
型推論で単一化(unification)ができずにコンパイルエラーとなる場合のエラーメッセージの改善
-
overrideフィールドが継承元classに存在しない場合のエラーメッセージの改善
-
文字列リテラルの閉じ忘れエラーメッセージの改善
-
字句解析の段階で発生するエラーメッセージの改善
クラスを書かずにいきなり関数が書けるようになる言語仕様です。プロポーザルは既に通っており、Haxe 4.1で導入される予定です。
Haxeでexternを定義する際にネイティブ側の識別子がHaxeの予約語と衝突している場合、 @:native メタデータを使ってHaxe側の識別子は別名にして問題を回避します。しかし現状は、匿名構造体型に対して @:native を指定しても無視されます。
typedef Parameter = {
@:native("default")
var defaultValue: Int;
}Issueとしては上がっていますが、今のところ対応予定がありません。
Haskell, Scala, C#などの言語では既に当たり前の話ですが、関数やメソッドの引数名として _ を指定すると無視することができます。
Haxeでも _ を引数名に指定することは可能ですが、今のところ無視はされずに参照可能となっています。これでも実害は小さいのですが、、Haxe 4.0から for のループ変数に指定した _ は無視されるようになったため、引数もこの挙動に合わせてほしいところです。
@RealyUniqueName Thanks for your comment! I fixed it.