null さんとのお付き合い

ちょっと暇つぶしに書きます。ついらーで null で盛り上がったので。


null ってめんどくさいですね。一番嫌いな例外は NullPointerException だという方も多いのではないのでしょうか。そんなことないか…

さて null のめんどくささは兎に角 null チェックです。

public void f<T>(T t) {
  if (t != null)
    g(t);
}
public void g<T>(T t) {
  if (t != null)
    h(t);
}
public void h<T>(T t) {
  ...

まあ極端な例ですしエラー処理も省略していますが。まじめに null チェック毎回してたらそりゃ大変です。

それで普通は「あーもうここどうせ null こないだろう」とかいって null チェックを省略し始めるのですが、

public void g<T>(T t) {
  h(t);
}

不届き物が現われて null かもしれない値を渡しはじめるわけです。

public void yamero(bool cond) {
  if (cond) h(null);
}

そんなわけでこのツイートを満たすのってとっても大変だと思うんですね。null が来るかも知れない箇所なんて、コードに変更が加わればすぐ変わるわけです。逆に null が来なくなることもあるかもしれません。そうなると保守的には全ての箇所で null チェックをするしかなくなるわけですが、そんなことしてたら人間が死んでしまいますし、null がこないことが分かっているのに null チェックをするのはコードとして妥当とは言いがたいものがあります。まあ不届き者が現れるのでそうも言ってられなくて…嗚呼!!

それでまあ、最近の IDE というのは賢いですから、アノテーション書いておけば静的検査してくれたりするんですが、それだとつまんないので、ちょっと Java を拡張してみます。

まず NotNullEvidence というクラスを追加しましょう。

public void g<T>(T t, NotNullEvidence ev) {
  h(t);
}

さらに、この NotNullEvidence クラスのインスタンスは「null チェックを行った際にしか取得できない」ことにしてしまいます。文法とかは適当です。

public void f<T>(T t) {
  if (t != null)
    g(t, getNotNullEvidence(t))
}

これで引数に対する「null じゃないこと」という事前条件をコメント等でなく型チェッカにより検査することができるようになりました。やったね!これだとどの値に対する evidence なのかわからないけどね!そういうのは依存型のある言語使ってね!

とはいえ、こんなの一々やってられないんで、null なんて初めから無ければよかったのです。逆に、null になりうる場所に「ここ null になるかもしれませんよ」と教えてあげるほうが楽チンなわけです。

というわけで「nullがなくなってNoneになりましたって、別に嬉しくないよね!」に対する回答としては、一言でいうなら「null を全部 None に置き換えれば、一々煩わしい事前条件に振り回されることも無くなって楽チンだよね!」だろうと思います。

そもそも null のある言語というのは、ADT というか Haskell 使っちゃいますが、全ての型に暗黙に値コンストラクタが追加される言語だと解釈できます。

-- これが
data Hoge = Huga
-- こうなる
data Hoge = Huga | Null

これは嬉しくないですね…値コンストラクタが増えるというのは状態が増えるということで、それは条件を無闇に増やされちゃってるということで…ヤメロー!ヤメロー!

とはいえ、Option だとか Maybe だとかで「失敗し得る計算」を表現したいんじゃなくて「空のようなものも対象とするだけの計算」を表現したいんだ、という場合に Option や Maybe を使うのはあまり筋が良いとはいえないですね。

そういう場合には、上記のコードのように、素直に空のようなものを表す値コンストラクタを追加してやればいいわけです。

まあたまにはこういう自明なことを書いてもいいのではないかと思った次第です。おしまい。