null安全でない言語は、もはやレガシー言語だを読んだ多くのRubyistは衝撃を受けたことだろう。そう、Rubyはオワコンなのだ。まだ始まってすらいないのに既に終わったのだ。クロムが少々混ざった程度の酸化アルミニウムよりも二酸化ケイ素の時代なのだ。まぁ霊薬も捨てがたいが。
だがし、かし。Rubyのポテンシャルはそんなものではないはずである。そう、たったひとつ魔法の呪文、次の一行を先頭に書けば、我らはNoMethodError
の呪縛から解放されるのだ。
def nil.method_missing(*_);nil;end
もうこれで、忌々しきNoMethodError: undefined method `なんか' for nil:NilClass
を見ることは無いだろう。我々はNoMethodError
を封印に成功し、より高次元のレベルに至ったのだ!
さて、よくわからない文章から始まりましたが、今回のポエムのテーマはRubyで nil安全 です。null安全 ではありません。だって、Rubyにはnull
がないんだもん。
null安全 のメリットに、NullPointerException
等の変数や戻り値がnull
であったが故のエラーを防げるというのがありました。Javaはヌルポばかりで困るというアレです。Rubyも他人事ではありません。変数や戻り値がnil
だったとき、アヒルのように鳴くことはできないので、やはりエラーになります。ただ、null
とは違いnil
も一つオブジェクトです。なので、出るエラーは、想定していた型が間違っていて、呼び出すべきメソッドがなかったことを意味するNoMethodError
になります。
そう、この魔法の呪文はnil
に対するNoMethodError
をなくす素晴らしいものです。もし、想定外でnil
だったとしても、nil
が返ってくるだけです。メソッドチェーンで繋いでも、nil
からまたnil
が返ってきて何のエラーも起きません。すごいと思いませんか?
気付いた人がいると思いますが、nilが元々持っているメソッド以外について全て&.
にしているような物です。考えようによっては、全てが常にMaybeモナドに包まれている、nullableになっている、とも言えます。nilableなんてgemもあるんですけど、言ってしまえば、強制的に常にこのgemで言うNilable(...)
っていると言うことです。
Rubyの世界ではもともと全部nilableです。そもそもnilableじゃない物なんて無かったのです。nil
から導かれる物はがnil
になる、そう、これは_null安全_の仕組みと同じです。となると…うん、そう、まさしくこれは null安全 もとい nil安全 と言ってもいいのではないでしょうか?
ということで、Rubyが null安全 じゃない、レガシーだ、オワコンだ、と言う人には、この魔法の呪文で、Rubyは nil安全 になる!と言い張りましょう。
勝ったな。
では、オチです。
さて、この方法、副作用があるんじゃ無いか?という前に、意味があるのか?を問わなければなりません。私が言い始めたのに何なのですが、あまり意味がないと思っています。なぜなら、魔法の呪文で nil安全 にはなりましたが、違う型であった場合は依然としてNoMethodError
が発生するからです。
x.quack
と書いたとき、私はx
がDuck
クラス、または、Duck
クラスっぽい何かのインスタンス(きっとそれはDuck
である1)を期待しています。そして、きっと"quack"
のような物が返ってくることを期待しています。魔法の呪文は、nil
であったらnil
が返ってくるので、何も処理できないで終わりました。それはいいかもしれませんが、これがもし別の何かだったら?そう、エラーになります。犬も猫もquack
することはできないのです。
Rubyにおいて動的型付けであるが故の特徴の一つが、ダッグ・タイピングができると言うことです。先ほどのx.quack
はToyDuck
クラスならきっとうまくいくでしょう。戻り値は"quack"
からややずれた物かも知れませんが、それは些細な問題です。実際に鳴いて貰うその時になって初めて、それがアヒルなのかを判断するため、それまではx
については何でもいいかのように扱えるのです。実行時にx
がアヒルになってさえいればいいのです。しかし、これには矛盾があります。私達はx.quack
と書く時、やはりx
がアヒルであることを常に期待します。期待していながらも、そのときまではわからない。ダッグ・タイピングの限界はここにあると思います。
つまり、x
がアヒルであることを期待するのであれば、やはり、x
がアヒルであると言うことを、実行時ではなく、期待を込めているその時にすぐさま知りたいのです。必要なのはCrystalのような静的型付けかPythonとmypyのような型ヒントと静的型チェックなのです。現在のRubyにはそれが欠けています。実行してみたらアヒルじゃ無かったと言って、NoMethodError
が発生してしまう可能性があります。静的な型チェックがなければ、nil
のNoMethodError
のみを解決しても何もならないと言うことです。そんな状態で nil安全 なんて言っても餡だけの饅頭みたいな物です。2
餡だけの饅頭…もうそれってただの餡なんじゃ…って思うかも知れませんが、逆に言うと null安全 じゃない型チェックは餡がない饅頭と言うことです。皮だけの饅頭…そりゃおいしいわけがありませんね。静的型付けや型ヒント+静的型チェックには null安全 があって初めておいしい饅頭になり得るのです。もし、null安全 が保証できないのであれば、Rubyのように初めから全部安全じゃない方がいいとすら私は思います。どうせ、静的に全てをチェックできないのであれば、煩わしい型など書かない方がいいのです。常に動的に移り変わる可能性を意識し、テストを十分にした方がいいのです。
将来、たぶんRuby3の時に、何かしらの型チェックシステムが新たに導入されると言われています。そのとき、null安全 ならぬ nil安全 がきっと導入されると期待しています。しかし、ダッグ・タイピングを捨てずにそれができるのか、そもそもダッグ・タイピングとは何だったのかを再度考えるきっかけになるのではないでしょうか。3
とにかくそれまでは、Rubyは null安全 でははないといちゃもんを付ける人達には、そももそRubyは型チェックすらできないからそれ以前の問題だ、と答えたいと思います。次元が違うと言うより、異世界の話をしていることです。
-
If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck. ↩
-
他にもこの方法には欠点があります。一つはnilableでないということが書けないこと。もう一つはコメントでご指摘頂いているとおり、メソッド側が内部で行う型チェックでのTypeErrorが防げないこと。どちらも静的型付けや型ヒント+静的型チェックが無ければ、事前チェックは不可能であり、現在のRubyの仕様では実現はできないと思われます。 ↩
-
mypyが成功するか否かに一番注目しているのは、Rubyの開発者達のような気がします。 ↩