はい、ポエムです。自分が正しかろうとは欠片も考えていません。異論や示唆は歓迎ですが、そう考えろと言っているわけではないので、そこはご容赦を。
前置き終わり。
さて、Nullは50億ドルの過ちだそうですね。
そして、それを回避するため、Maybe / Nullable / Optional などの言語機能が生まれ、各言語に埋め込まれています。 それは、大変喜ばしいこと、だと思います。
なんですが、どうもどうも、僕はどうしてもしっくりこないのです。これら全てに対して。Nullと同じように。
Nullは何がダメなんだっけ?
Nullがダメな理由、ってなんなんでしょう。よく言われる批判として、以下があるのかなぁと思っています。
①Nullは型ではない
Nullと言っていますが、こいつはNullポインタ、でございます。 からの「何か」を指すポインタ。つまり、型がない。
CalssHoge のnullも、 ClassFuga のnullも、同じもので、それをに特別な役割を与えることができないのです。
②あるメソッドがNullになるかどうかの判断ができない
Nullを普通に返していると、「そのメソッドがNullを返すかどうか」が判断できなくなります。
結果として、あらゆるメソッドに対して、Nullチェックを行うことになってしまい、コードが if (x == null){ return; } だらけになります。
だいたい、これくらいでしょうか。
Optionalが解決するのは②だけ
こう考えると、Optionalなどのパターンが解決するのは②だけです。
確かに、 Optional の明示によって、解決は可能になります。Optionalを返すものは、ハンドリングをしないといけない(ハンドリングしないと元の型に代入できませんからね)。
うん、素晴らしい。
①の「どの方のNullか分からない」という問題も解決はしますが、本当に解決したいのは「Nullに型独自の処理をさせたい」なので、残ったままです。 でもまぁ、②が解決できれば、いいんじゃないの?
なぜ②を解決しただけでは気持ち悪いのか
そこが僕の違和感です。何が気持ち悪いのか、大きく2つあります。
結局 if 分相当の処理を書いてしまってる
Optional にせよ、 Either にせよ、対照Objectが無かった場合の処理を、Optional を返していれば書く必要があります。
記法が洗練されただけで、 Optional は if乱発を本質的には解決していないのです。
いや、むしろ、 記法が簡潔になった分、逆にあらゆるメソッドに気軽に Optional がついてしまい、コードがより汚くなってしまっている傾向がある とさえ感じています( swift が特に顕著だと思う)。
とっても手続き的に見える
当たり前のように呼び出し側で Optional をチェックしていますが、本当にその責務は呼び出し元が負うべきなのでしょうか?
呼び出し元が、問い合わせで帰ってきた値に対して空チェックをし、空の場合は何らかの処理をする、というのは非常に手続き型プログラミング的です。*1
オブジェクト指向プログラミングの観点からいうと、 空の時にどう振る舞うべき かは、可能な限りそのオブジェクトが持つべきだと思います。 「求めるな、命じよ(Don't Ask, Order)」のオブジェクト指向の原則的観点から言うと、空だろうがなんだろうが、提供されているインタフェースに関して、命じられた命令を返すのが、正しい姿ではないか、と思ってしまうのです。
つまり、NullObject をちゃんと使いたい
なので、NullObjectをちゃんと使いたいのです。
NullObjectは、ただ単に equals で問い合わせれば Null かどうかが分かるだけの静的な値では無いと思っています。
Nullである時、つまりデータ取得を失敗した時や、初期化時や、その他諸々、まだ値が空の時にも、責任持ってそのオブジェクトとしての責務を果たすだけの機能を持つ。
それがNullObjectなんじゃないかと。 もしその責務を果たすために親が何かを与えてやることが必要なのであれば、それはオブジェクトの設計そのものが間違っていたり、歪んでいたりすると思うのです。
勿論、その時に逃げ道は必要ですが、それを糖衣構文化することは、言語としては避けたほうが良いんじゃないのかなぁと、そんなふうに思うのです。
以上、ポエムでした。