Haxeで仕方なくnullと付き合う

Haxeは現時点で選択しうるaltJSの中では型システムが一番出来が良く、代数的データ型(Haxeではenum)が扱える点が素晴らしい。しかし、元々はFlashを前提とし、今はマルチターゲット(JavaScriptPHPC++C#等)にコンパイルする言語として設計されているため、どうしてもnulを扱わなければならない。

これはHaxeに限った話ではなく、F#やScalaなどでも同様の問題があるのだが、これらの言語と比較すると、Haxeはすこしnullが全面に出てしまっている(むしろnullを許容する言語設計にすることで、学習時の敷居を下げているのではないかとも思えるが…)ので、nullを根絶したい勢にすると、ちょっとぐぬぬとなる面がある。

少し前置きが長くなったが、Haxeで仕方なくnullを付き合うための方法の1つを書いてみる。

そもそも、nullを使わないコードとは?

Option型(言語によってはMaybe型)を使うコードである。Option型とは、「値が存在しない可能性があるデータ」を表現するための型である。

最近普及期に入ってきた型システムがリッチな言語(Scala、F#、OCamlHaskell等)では当たり前に利用できる。

要は「nullの代わりに使いましょう」という型なのだが、nullのと大きな違いは、型安全である(「いわゆるnullチェック」にあたるコードを書かないとコンパイルすら通らない)という利点がある。nullを許容する場合だと、nullチェックを書かなくてもコンパイルが通ってしまうため、ケアレスミスによるバグが混在してしまう可能性がある。

蛇足ではあるが、リッチな型システムを使うメリットは「計算機がチェックできる範囲を広げることで、人間が楽をできる」ことであるので、食わず嫌いをして使わないのは勿体がない。

HaxeのOption型

Haxe 3.0から、haxe.ds.Optionという、まさにそのものが標準ライブラリとして用意された。(ちなみにEitherはない。)

Haxeでnullを使わざるをえないところ

大きく2つある。

1. デフォルト引数

Haxeは関数やメソッドにデフォルト引数が設定できるのだが、デフォルト引数にOption型を指定することができない。そのため、どうしても次のような定義を書かざるをえないことがある。

function foo(option: String = null): Void { ... }
2. 環境依存や外部ライブラリを利用するケース

JavaScriptでDOMやjQueryなどを扱わなければならない場合に多発する。

var elem = js.Browser.document.querySelector("#hoge");
// valueにはnullが入る可能性
function setProperty(name: String, value: String): Void {
    var node = new JQuery("#hoge");
    node.prop(name, value);
}

これらの他にも、コールバック関数で受け取る値がnullというようなケースも多々ある。

ちょっとした工夫をする

まず次のようなHelperを作成する。ScalaのOption型まんまである。

class OptionHelper {
    public static function create<T>(x: T): Option<T> {
        return (x != null) ? Some(x) : None;
    }

    public static function getOrElse<T>(a: Option<T>, b: T): T {
        return switch (a) {
            case Some(x): x;
            case None: b;
        }
    }
}

これだけあれば、結構いける。どうしてもコーディング規約的なものになってしまうが、無いものは仕方がない。

function foo(rawOption: String = null): Void {
    var option = OptionHelper.create(rawOption);
    ...
}
using OptionHelper; //using mixin

function setProperty(name: String, value: Option<String>): Void {
    var node = new JQuery("#hoge");
    node.prop(name, value.getOrElse(""));
}

OptionHelperにmap()等のメソッドを追加すると更に便利である。本当はこのあたりを全て標準ライブラリで用意してもらいたい…。