2.7 Dynamic

目次に戻る

2.7 Dynamic

Haxeは静的型システムをもっているが、Dynamic型を使えば 事実上この型システムを切ることができる。 dynamic値はどの型にも代入できるし、どの型の値もDynamic型に代入できる。 これには次の弱点がある。

  • コンパイラは変数への代入、関数呼び出し、その他のconstructsで、 どの型が使われるべきかという型チェックができなくなる。
  • 特に静的型システムを使うターゲットにおいて、最適化が使われなくなる。
  • よくあるエラー(例えばtypoとか)をコンパイル時に見つけることができなくなる。 これらは実行時エラーとなる。
  • Dead Code Eliminationは、 Dynamicを通して使われると使用されているフィールドを検出できない。

Dynamicの利用によって実行時にエラーが出る例を考えつくのは簡単だ。 静的ターゲットで次の2行をコンパイルする。

var d:Dynamic = 1;
d.foo;

コンパイルしたプログラムをFlash Playerで実行すると Property foo not found on Number and there is no default value というエラーが出る。Dynamicにしなければこのエラーはコンパイル時に検出されたはずだ。

Tribia: Haxe3以前のDynamic Ingerface

Haxe3コンパイラは何かの型をDynamic型と推論することはない。so users must be explicit about it (訳注:訳せない)。以前のバージョンのHaxeは異なる型が混ざった配列、例えば[1, true, "foo"]Array<Dynamic>と推論していた。我々はこの振る舞いは多くの問題を引き起こすことがわかったんで、Haxe3ではやめた。

Dynamicの使用は最小限にするべきだ。通常は他にもっといい方法がある。しかし、稀にDynamicを使うことが現実的である場合もある。Haxe Reflection APIの一部ではDynamicを使っている。コンパイル時にはわからないようなカスタムデータ構造を扱うような場合には、それが最良の選択になることもある。

Dynamic behaves in a special way when being unified with a monomorph(訳注:unifiedの訳に困る). MonomorphはDynamicになることはない。このため次のような例にみられるような意外な挙動となる。

class Main {
  static function main() {
    var jsonData = '[1, 2, 3]';
    var json = haxe.Json.parse(jsonData);
    $type(json); // Unknown<0>
    for (i in 0...json.length) {
      // Array access is not allowed on
      // {+ length : Int }
      trace(json[0]);
    }
  }
}

Json.parseの返り値の型はDynamicであるが、ローカル変数jsonDynamicにならず、monomorphのままである。そして、json.lengthとフィールドアクセスしたときに、anonymous structure型推論される。この結果、次のjson[0]という配列へのアクセスはエラーとなる。これを避けるためには、変数jsonを陽にDynamicとして宣言、つまりvar json:Dynamicとすればよい。

Trivia: 標準ライブラリにおけるDynamic

DynamicはHaxe3以前では標準ライブラリで頻繁に使われていた。Haxeの型システムを改良し続けた結果、Dynamicの使用箇所はHaxe3のリリースまでに少なくなった。

訳注: 雑な訳だなあ