Haxeで仕方なくnullと付き合う
Haxeは現時点で選択しうるaltJSの中では型システムが一番出来が良く、代数的データ型(Haxeではenum)が扱える点が素晴らしい。しかし、元々はFlashを前提とし、今はマルチターゲット(JavaScript、PHP、C++、C#等)にコンパイルする言語として設計されているため、どうしてもnulを扱わなければならない。
これはHaxeに限った話ではなく、F#やScalaなどでも同様の問題があるのだが、これらの言語と比較すると、Haxeはすこしnullが全面に出てしまっている(むしろnullを許容する言語設計にすることで、学習時の敷居を下げているのではないかとも思えるが…)ので、nullを根絶したい勢にすると、ちょっとぐぬぬとなる面がある。
少し前置きが長くなったが、Haxeで仕方なくnullを付き合うための方法の1つを書いてみる。
そもそも、nullを使わないコードとは?
Option型(言語によってはMaybe型)を使うコードである。Option型とは、「値が存在しない可能性があるデータ」を表現するための型である。
最近普及期に入ってきた型システムがリッチな言語(Scala、F#、OCaml、Haskell等)では当たり前に利用できる。
要は「nullの代わりに使いましょう」という型なのだが、nullのと大きな違いは、型安全である(「いわゆるnullチェック」にあたるコードを書かないとコンパイルすら通らない)という利点がある。nullを許容する場合だと、nullチェックを書かなくてもコンパイルが通ってしまうため、ケアレスミスによるバグが混在してしまう可能性がある。
蛇足ではあるが、リッチな型システムを使うメリットは「計算機がチェックできる範囲を広げることで、人間が楽をできる」ことであるので、食わず嫌いをして使わないのは勿体がない。
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()等のメソッドを追加すると更に便利である。本当はこのあたりを全て標準ライブラリで用意してもらいたい…。