MaybeモナドっぽいRubyの書き方
Haskellを勉強しながら、ほとんどの機能はRubyでも似たようなことが出来るな、と感じている。
でも、無限リストや無限木構造の遅延評価とか、Rubyではできないこともやっぱりある。
これはうらやましい!と思った機能の一つに、Maybeモナドがある。
hogeにdo_itして、その結果にdo_thatして、さらにその結果にjust_do_itした結果、というのを
hoge >>= do_it >>= do_that >>= just_do_it
というように表せるのだが、このプロセスの途中で結果がNothing (nilみたいなもの)になる場合があっても、そんな場合をハンドリングするコードを加えないで、
hoge >>= do_it >>= do_that >>= just_do_it
のままでOK。なぜなら、プロセスの途中でNothingになった場合は、Nothingにその後どんな操作をしてもNothingというルールがあるので、結局、式全体の結果もNothingになるだけで、エラーにならない。これは書くのが楽だ。
Rubyでいうなら、
hoge.do_it.do_that.just_do_it
といった感じか。
でもRubyだと、途中でnilが返る可能性があるときは、nilのハンドリングを
無視するわけにはいかず、
(x=hoge.do_it).nil? ? nil : (y=x.do_that).nil? ? nil : y.just_do_it
とか、かっこわるいことになってしまう。
そこで、RubyでMaybeモナドっぽい書き方をする方法を考えてみた。
まず、どんなメソッドを呼ばれても、nilを返すオブジェクト"NULL"を用意する。
class NullClass def method_missing(message, *arg) nil end end NULL= NullClass.new
で、こうする。
((hoge.do_it || NULL).do_that || NULL).just_do_it
まあ完全にnilハンドリングが消えたとはいえないけど、だいぶましなんじゃないでしょうか?
このやりかたのいいところは、とりあえず何も考えずに、
hoge.do_it.do_that.just_do_it
といったコードを書いておいて、あとからnilハンドリングが必要なところを、
( ... || NULL)で括っていけばよい、ということです。
このワザで自分のコード中のメソッドをモノによっては半分程度に短くすることに成功しています。
P.S.
上のNULLの定義の代わりに、どんなメソッドを呼んでも自分自身(NULL)を返すオブジェクトというのも有用かも、
と思った。
class NullClass def method_missing(message, *arg) NULL end end NULL= NullClass.new
これの論理値評価をfalseに出来れば||と組み合わせて使えるのでかなり便利かも、と思ったけど、
nil, false以外のオブジェクトの論理値評価をfalseにする方法が調べた限り見つからなかった。
P.P.S
ぼんやり考えてる人から、
class NilClass def method_missing(message, *arg) nil end end
で、
hoge.do_it.do_that.just_do_it
ってかけるよ、という指摘が。
ううーん、確かに。これはかなり強力なワザかも。