JSXよりHaxeがイケてる5つの理由(実践編) もしくは Real World Haxe

JSXがリリースされて1週間ぐらい立ちました。ガシガシ進化が進んでてすごいですねー。
おかげで、文法と適用範囲が似てるHaxeにも注目を集まっている気がします。なので、今日はそのHaxeを実際のプロジェクトに適用した事例について紹介したいと思います。題してReal World Haxeです。

プロジェクト概要

プロジェクトの概要は、前にosiireさんが2009-10-21で書いているのでそのまま引用します。

某自動車販売会社用のWebサービス
サーバーサイドを全部OCamlで作成。OCaml+MySQL。既に稼働中のサーバーへ導入しなければならず、その環境が少し古かった(debian serge)ので一瞬焦ったが、ちゃんとOCaml開発環境は揃った。初めてpa_monadを導入したが、とても便利だった。重回帰分析をOCamlで実装した(Cで書くのがイヤだったから)。OCamlのコードはトータル4000行程。

上記の記事ではサーバサイドにしか触れられてませんが、クライアントサイドには過去の販売状況をグラフ表示するためにFlashが用いられていました。 そのFlashを開発するにあたりHaxeが採用されました。
コードサイズはトータルで8000行程度で、開発体制は4名ほどでした。まあ一人はアルバイト(ボク)でしたが。

イケてる理由1) 型推論が強力

JSXにも型推論が実装されていますが、関数呼び出しの引数に無名関数リテラルを渡したときに型注釈を省略できる、といった程度の限定的なものです。今後、型推論できる箇所は増えていくと思いますが、後付けで型推論を追加しているため限定的なものに留まらざるをえないと思います。
一方、Haxeは最初から型推論を意図して設計されているため、ほぼ全ての箇所で型を省略できます。

// サーバからT型のデータを取得するクラス
class Request<T> {
  function new(url : String, onLoad : T -> void) {
    // ...
  }
}

// サーバサイドからMarket情報を取得するクラス
class MarketRequest extends Request<Market> { 
  // superに渡すため引数の型が推論可能
  function new(url, onLoad) {  
     super(url, onLoad);
     var http = new Http;
     // onErrorの型から無名関数の型を推論可能
     http.onError = function(error){
     };
   }
}

ただ、あまりに省略すると読みづらくなってしまうので、

  • 型が分かりにくい引数には型注釈をつける
  • ある程度複雑なクラスは型注釈をつける

などの対応をしていました。

イケてる理由2) 関数レベルの多相性

JSXもテンプレートをサポートするようになりましたが、現時点ではクラスのみが型パラメータを受け取ることができ、関数は受け取ることができません。そのため配列をmapして別の型の配列を作り出すことはできません。

// JSXだよ!
// 内部データをグラフ上の点に写像したい
var data : Array.<MyData> = …
// mapの返り値はArray.<MyData>固定
var pts : Array.<Point> = 
  data.map<Point>(function(x : MyData) : Point {  
    return conv_my_data_to_point(x); 
  })

一方、Haxeでは関数ごとに型パラメータを持たせれるため、別の型の配列を作り出すことができます。

// Haxeだよ
// 元データから日付と値のペアを作る
var xs : List<Pair<Int, Int>> = 
     Lambda.map(data, 
                          function (x) { 
                             return new Pair(x.getDate(), x.getValue()); })

イケてる理由3) Option型とEither型

enumのすばらしさに書くつもりだったんですが、実際にgrepしてみたらほとんど使われていませんでした。せいぜい、グラフの描画オプション程度でした。
しかしenumのおかげでOption型とEither型を定義することができ、nullを使うのを避けれました。

// 失敗するかもしれない計算を表わす
enum Option<T>{        
  None;
  Some( v : T);
}

// 失敗した場合のメッセージも付与する
enum Either<A, B>{       
  Left( v : A );    
  Right( v : B );
}

イケてる理由4) 構造的部分型による柔軟なクラス定義

通常の名前によるインタフェースはまったく使わずに、すべて箇所で構造的部分を使いました。たとえば、X軸を描画するためのクラスの型は次のように定義されています。

typedef XAxis = {
  function draw(state : TState) : Void;
}

そして、各グラフクラスは、このXAxis型を使って描画処理を実装します。

class SimpleGraph {
   public var xaxis : XAxis
   public function draw(state : TState){
      xaxis.draw(state);
      ….
   }
}

当然SimpleGraphクラスを使う場合はxaxisフィールドへの代入が必要です。ただし、構造的部分型のおかげで、クラスを宣言する必要なくその場でオブジェクトを作ることができます。

var graph = new SimpleGraph
graph.xaxis = {
   draw : function(state) {
     ...
   }
};

本来は、あとからXAxisWithSomeGreatFeatureみたいなのを定義できる拡張性を残すための設計だったのですが、実際はほとんど拡張が必要になることはありませんでした。構造的部分型を使ったことで記述量も増えたわけでもないですし、可読性・安全性も損なわれていないのでいいんじゃないですかね。

イケてる理由5) 開発環境、ドキュメントが揃っている

開発環境も充実しており、Windows+FlashDevelopでも、Windows + VirtualBox + Linux + emacsでも問題なく開発できました。(後者はボクです)
また公式ドキュメントも、関数型プログラミングのバックグラウンドを持っていない人でも問題なく書ける程度にはそろっていました。

残念だったところ

当然いいところだけでなく、残念だったところもいくつかありました。

Haxe Remotingが使えなかった

当初はサーバサイドもHaxeで生成したPHPコードを用いて、通信をHaxe Remotingでやるつもりでした。しかし、稼働中のサーバにリリースする関係上、OSが古く、パッケージシステムからPHP4しかインストールできずあきらめました。
自分でビルドするという選択肢も検討しましたが、さすがに面倒だったので、結局OCamlになりました。

Flexのライブラリが使えない

アプリケーションを作る際に生のFlashライブラリだけだと厳しいので、Flexを利用することを検討しました。 ただ初期処理を走らせる必要があるらしく、ライブラリをリンクするだけでは利用できませんでした。
このFlexの生成したswfを逆アセンブルしてそれを突き止めるか否か、という話になりかけたので、あきらめてAswingというswing風のライブラリを利用しまた。

まとめ

Haxeは実プロジェクトへの適用事例もあるし、みんな使うといいと思うよ。