Rubyでは、ついこの間の2.3のリリースまで、 nil と false を区別する方法がありませんでした。
「 nil か false 」とそれ以外を区別することはできます。 if とか unless でも良いですし、 && でも良いです。Rubyの構文の中には、真理値に応じてなんらかの処理をしたりしなかったりするものがありますので、「偽」である「 nil または false 」と、「真」である「それ以外の値」は区別できるのです。が、「偽」同士の nil と false は区別することができませんでした。
と、進めると、Ruby初心者の皆さんは混乱するかもしれませんね。 nil? があるじゃないかと。 x == nil でいいじゃないかと。しかし、こいつらはメソッドでありユーザーが自由に再定義できるので、一般的な話をするときには、信頼してはいけません。「 nil? で nil かどうかをテストできる」と言うためには「ただし nil? は再定義されていないものとする」と前提条件を示す必要があります。
# x.nil? を殺す def nil.nil? false end # x == nil を殺す def nil.==(other) false end # nil.object_id を殺す def nil.object_id false.__id__ end # nil.class == NilClass を殺す def nil.class ::FalseClass end
さて、それではどうするかというと、Safe Navigation Operatorを使います。 &. を使うと、レシーバが nil のときのみメソッドが呼ばれません。つまり、 false と nil を区別することができるようになったのです。さらに、一昨日くらいに気づいたのですが、 nil のときには引数の評価もされません。なんて便利!
こんな感じでどうでしょうか。
def is_nil?(value) result = true value&.__id__(result = false) result rescue result end
&. を使うという方法には3年くらい前に気づいていたのですが、引数が評価されたりされなかったりすることには気づいていなかったため、メソッド呼び出しがあったかどうかをメソッドの定義に依存する形でしか判定できず、特定のメソッドの定義を前提とするのがちょっとずるいなあって思ってずっと眠らせていたTipsでした。一昨日くらいに気づいたブレイクスルーとして、これは引数の評価だけ見るので、メソッドがあってもなくてもかまわないので、前提が十分に少なくなったと言えると考えています。
他に方法があったり、上の is_nil? を騙す方法を思いついた方は、ぜひ教えてください。