Rubyでnilとfalseを区別する方法

補足があります。 Rubyでnilとfalseを区別する方法 - soutaroブログ

Rubyでは、ついこの間の2.3のリリースまで、 nilfalse を区別する方法がありませんでした。

nilfalse 」とそれ以外を区別することはできます。 if とか unless でも良いですし、 && でも良いです。Rubyの構文の中には、真理値に応じてなんらかの処理をしたりしなかったりするものがありますので、「偽」である「 nil または false 」と、「真」である「それ以外の値」は区別できるのです。が、「偽」同士の nilfalse は区別することができませんでした。

と、進めると、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 のときのみメソッドが呼ばれません。つまり、 falsenil を区別することができるようになったのです。さらに、一昨日くらいに気づいたのですが、 nil のときには引数の評価もされません。なんて便利!

こんな感じでどうでしょうか。

def is_nil?(value)
  result = true
  value&.__id__(result = false)
  result
rescue
  result
end

&. を使うという方法には3年くらい前に気づいていたのですが、引数が評価されたりされなかったりすることには気づいていなかったため、メソッド呼び出しがあったかどうかをメソッドの定義に依存する形でしか判定できず、特定のメソッドの定義を前提とするのがちょっとずるいなあって思ってずっと眠らせていたTipsでした。一昨日くらいに気づいたブレイクスルーとして、これは引数の評価だけ見るので、メソッドがあってもなくてもかまわないので、前提が十分に少なくなったと言えると考えています。

他に方法があったり、上の is_nil? を騙す方法を思いついた方は、ぜひ教えてください。