Haxe 4.2で導入される Module-level fields を試した

Haxe 4.2は順調にいけば、2020年10月14日にリリースされるはずです。とはいえ、Haxeのマイナーバージョンアップのリリースは、バグフィックスも含めると2か月前後ぐらい後ろ倒しになる印象なので、2021年初に使えるようになってるかなーぐらいでしょう。

Haxe 4.2の目玉機能の1つとして、Module-level fieldsが導入されます。雑に言ってしまうと、Go言語に近い書き方が導入されます。

haxe.org

この機能は既にマージ済みとなっているので、nightlyビルドで動作確認することができます。

とりあえず、次のコードはコンパイルできて、ちゃんと動作します。

// Main.hx
final version = 4;

function main() {
    trace("Hello from version " + version);
}

このコードは、次のコードと等価です。要はシンタックスシュガーに過ぎません。要点としては、

  • クラス名はモジュールファイル名から設定される(ここではMain.hxなので、Mainになっている)
  • すべてpublic staticとして扱われる
class Main {
    public static final version = 4;

    public static function main() {
        trace("Hello from version " + version);
    }
}

このような変換ルールなので、例えばこのように関数定義なしにロジックを記述することはできません。

// このコードはコンパイルが通らない
final version = 4;

trace("Hello from version " + version);

サブモジュールを作る/使う際も、特に難しいことはありません。

Haxeを知っている人には、import Lib1してるだけなのに、なんでadd(1, 2);って書けるの…?となる部分がありますが、これはimport Lib1.*しているのと同じ扱いになっていようです(ちゃんと情報を読み込んでないので本当に*シンタックスシュガーなのか自信がない)。

// Main.hx
import Lib1;

function main() {
    final result = add(1, 2);
    trace(result);
}
// Lib1.hx
function add(a:Int, b:Int):Int {
    return a + b;
}

モジュールの初期化を行いたい場合は、__init__()関数を使います。急にPython感が出ますが、実は少なくともHaxe2の時代には存在していた機能です(Haxe2以前は触ったことがないのでわかりません…)。

// Lib1.hx
var value:Int;

function __init__() {
    value = 10;
}

ただし、残念ながら__init__()final変数の初期化はできないようです。シンタックスシュガーであることを考えると仕方ない感じがします。

// これはコンパイルが通らない
final value:Int;

function __init__() {
    value = 10;
}

また、モジュール外に公開したくないものはprivate修飾子を指定します。

// Lib1.hx
private function add(a:Int, b:Int):Int {
    return a + b;
}

全的的に、かなり直感的に書けるようになった印象です。いちいちclassとかstaticと書かなくて良くなったことは、特定の層には刺さりそうな感じがします。

強いて気になる点を挙げると、クラス構文の場合とは、importの挙動が異なることと、publicデフォルトなのかprivateデフォルトなのかに差異があることが、気になるといえば気になります。