Ruby
ポエム

Rubyでnil安全?

More than 1 year has passed since last update.

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と書いたとき、私はxDuckクラス、または、Duckクラスっぽい何かのインスタンス(きっとそれはDuckである1)を期待しています。そして、きっと"quack"のような物が返ってくることを期待しています。魔法の呪文は、nilであったらnilが返ってくるので、何も処理できないで終わりました。それはいいかもしれませんが、これがもし別の何かだったら?そう、エラーになります。犬も猫もquackすることはできないのです。

Rubyにおいて動的型付けであるが故の特徴の一つが、ダッグ・タイピングができると言うことです。先ほどのx.quackToyDuckクラスならきっとうまくいくでしょう。戻り値は"quack"からややずれた物かも知れませんが、それは些細な問題です。実際に鳴いて貰うその時になって初めて、それがアヒルなのかを判断するため、それまではxについては何でもいいかのように扱えるのです。実行時にxがアヒルになってさえいればいいのです。しかし、これには矛盾があります。私達はx.quackと書く時、やはりxがアヒルであることを常に期待します。期待していながらも、そのときまではわからない。ダッグ・タイピングの限界はここにあると思います。

つまり、xがアヒルであることを期待するのであれば、やはり、xがアヒルであると言うことを、実行時ではなく、期待を込めているその時にすぐさま知りたいのです。必要なのはCrystalのような静的型付けかPythonとmypyのような型ヒントと静的型チェックなのです。現在のRubyにはそれが欠けています。実行してみたらアヒルじゃ無かったと言って、NoMethodErrorが発生してしまう可能性があります。静的な型チェックがなければ、nilNoMethodErrorのみを解決しても何もならないと言うことです。そんな状態で nil安全 なんて言っても餡だけの饅頭みたいな物です。2

餡だけの饅頭…もうそれってただの餡なんじゃ…って思うかも知れませんが、逆に言うと null安全 じゃない型チェックは餡がない饅頭と言うことです。皮だけの饅頭…そりゃおいしいわけがありませんね。静的型付けや型ヒント+静的型チェックには null安全 があって初めておいしい饅頭になり得るのです。もし、null安全 が保証できないのであれば、Rubyのように初めから全部安全じゃない方がいいとすら私は思います。どうせ、静的に全てをチェックできないのであれば、煩わしい型など書かない方がいいのです。常に動的に移り変わる可能性を意識し、テストを十分にした方がいいのです。

将来、たぶんRuby3の時に、何かしらの型チェックシステムが新たに導入されると言われています。そのとき、null安全 ならぬ nil安全 がきっと導入されると期待しています。しかし、ダッグ・タイピングを捨てずにそれができるのか、そもそもダッグ・タイピングとは何だったのかを再度考えるきっかけになるのではないでしょうか。3

とにかくそれまでは、Rubyは null安全 でははないといちゃもんを付ける人達には、そももそRubyは型チェックすらできないからそれ以前の問題だ、と答えたいと思います。次元が違うと言うより、異世界の話をしていることです。





  1. If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck. 



  2. 他にもこの方法には欠点があります。一つはnilableでないということが書けないこと。もう一つはコメントでご指摘頂いているとおり、メソッド側が内部で行う型チェックでのTypeErrorが防げないこと。どちらも静的型付けや型ヒント+静的型チェックが無ければ、事前チェックは不可能であり、現在のRubyの仕様では実現はできないと思われます。 



  3. mypyが成功するか否かに一番注目しているのは、Rubyの開発者達のような気がします。