Re: HaxeとTypeScriptを両方使ってみた感想
HaxeとTypeScriptを両方使ってみた感想 - ジンジャー研究室 に対してのコメント。コメント欄ではレスを書くには余白が狭すぎです。
if式について
HaxeのifはBool型しか受け付けないため、存在判定はnullとの比較が必須になる。JavaScriptからHaxeに移行するとifの度にコードが膨れ上がってしまう。
同じく、&&と||もBool型でないと使えない。これは正直とても不便だ。
生JavaScriptでもたまに問題になる暗黙の型変換でfalseと判定されてしまうケースを考えると、Boolしか受け付けないことには利点があります。私はむしろHaxeの考え方の方が正しいと思っています。
// JavaScript var i = 0; if (i) { // こっちは通らない } else { // こっちが通ってしまう! }
enumとパターンマッチ
nullチェックの話とも絡みます。
当初の思惑としては、これがHaxeの大きなアドバンテージになることを期待していたのだが、結局あまり使っていないのでどっちでも良いような気がしている。
Scalaみたいなことがやりたかったのだが、Option型はnullの存在によって破綻したし、Either型はJSONシリアライズできないという弱点があった。
Haxeのnullの扱い(typedef Null<T> = T)に関しては言いたいことはわからないでもないんですが、それでもenumを使わないのは非常にもったいないです。
以下のようなものを定義してみては如何でしょうか?
// Haxe enum Option<T> { Some(x : T); None; }
// Haxe // inlineを付けてインライン展開するようにしてありますが、このあたりは好み class OptionTools { public static inline function isEmpty<T>(value : Option<T>) { return if (value == null) { true; } else switch (value) { case Some(_): false; case None: true; } } public static inline function getOrElse<T>(value : Option<T>, defaultValue : T) { return if (value == null) { defaultValue ; } else switch (value) { case Some(x): x; case None: defaultValue ; } } public static inline function ifSome<T>(value : Option<T>, fn : T -> Void) { if (value != null) switch (value) { case Some(x): fn(x); case None: } } // getOrThrow()とかifNone()とか派生はいくらでも }
使用例
// Haxe using OptionTools; class Sample { static function main() { var value1 = Option.Some('hoge'); value1.ifSome(function (x) js.Lib.alert(x)); // using mixinならnullでもいけちゃう(良い例ではないですが…) var value2 : Option<String> = null; js.Lib.alert(value2.getOrElse('foo')); } }
こんな感じの物を用意してしまえば、本当にnullチェックが必要な個所は生JavaScriptやDOMをたたく箇所と、Optional Arguments使ってるところぐらいでしょうか。
ついでなので、Optional Argumentsについて触れておきます。私は最近、Optional ArgumentsについてはF# のdefaultArgみたいに以下のように書いてしまっています。
// Haxe function add(a : Int, ?b : Int) { var b = (b != null) ? b : 0; return a + b; }
でもよく考えたら、defaultArgsみたいなinline関数作った方がなお良い気がしますね。
連想配列の扱い
ここでは触れないが連想配列の扱いもかなり面倒だった。(Hashクラスがあるが、連想配列との互換がない)
もちろんuntypedなどで回避は出来るが、そのたびに記述が冗長になってしまう。
JavaScriptの連想配列(Object)を扱いたい場合はDynamicで良いのではないでしょうか?実はHaxeのDynamicには型パラメータを指定することができるので、JavaScriptとの相互運用をする場合には実用上十分ではないかと思います。
// Haxe var hash : Dynamic<String> = getJsHashMap(); //JavaScript側でHashMapみたく使ってるObjectを取得 var v1 = hash.hoge; // 型推論でちゃんとStringになる var v2 = hash.fuga;
なお、Dynamic型をHashのようにkeyでループを回すには以下のように書きます。
// Haxe var data : Dynamic = { foo: 1, bar: 2 }; for (key in Reflect.fields(data)) { var value = Reflect.field(data, key); }
蛇足ですが、Hashではなくtypedef(TypeScriptではinterface)で型を定義できてしまう場合は、当然そちらの方がよいです。