前置き: nil == falseの結果はfalse
Rubyの本には「条件分岐でnilとfalse以外のすべてのオブジェクトがtrueとみなされます」と書かれているのが定番です。
しかし以下のとおり、nilとfalseは別のオブジェクトです。
nil == false #=> false
nil?も同じことをおっしゃっています。
nil.nil? #=> true
false.nil? #=> false
true.nil? #=> false
nilとfalseが同一視されるのは、あくまで条件分岐(ifやcase)のときです。
a = nil
if a
"true!"
else
"false!"
end
#=> "false!"
上のようにaをnilからfalseに変えても結果は変わりません。
Rubyにtrue?メソッドやfalse?メソッドはない
ちなみにtrue?やfalse?といった冗長かつ悪手にしかならないメソッドはデフォルトのRubyにはありません。
true.true? #=> NoMethodError: undefined method `true?' for true:TrueClass
false.false? #=> NoMethodError: undefined method `false?' for false:FalseClass
if a == trueのようにわざわざ== trueだの== falseだのtrue?だのfalse?だのをモロ出しに書くことを避けるのがRuby流です。
Ruby作者のお気持ちを、これまで見かけた発言などから想像してみました。仮にtrue?やfalse?が「あり」だとすると、それを当てにしてたとえば以下のような無節操かつ欲張りさんなメソッドを書きまくって二値論理をぶち壊しにする人が続出するかもしれないということなのだと思います(外してたらすみません)。
trueかfalseかnilを返し、しかもtrueとfalseとnilの意味が異なる😨trueかfalseかnilの他にも値を返し、しかもそれぞれ全部意味が異なる😰
まあnil?があるからfalseとnilの区別はやろうと思えばやれてしまうし、そうするしかない感じのメソッドもあったりしますが、あまりよろしくなさそうですね。
Ruby作者は少なくとも「Booleanクラスは作らない」と言ってたと思います(理由はちょっと違いますが↓)。
@kzhk @makotokuwata Booleanクラスが欲しい人はduck typing的に悪手になるkind_ofチェックしたい人なので、Ruby言語デザイン的には導入しない方がユーザーを誘導できて吉だと考えます。
— Yukihiro Matz (@yukihiro_matz) March 29, 2014
参考: !!はRuboCopに怒られる
一部地域で!!を使ってtrue/falseに揃える人もいると聞きましたが、デフォルトではRuboCopに怒られます。
# frozen_string_literal: true
a = false
'false' if !!a
# 上にRuboCopをかける
#=> test.rb:4:12: C: Style/DoubleNegation: Avoid the use of double negation (!!).
'false' if !!a
参考: Style/DoubleNegation -- Style :: RuboCop Docs
RUboCopのStyle/DoubleNegationでは「!!somethingと!something.nil?の結果は同じではない」と説明しています。ごもっともです。
Rubyは「ナマのtrue/false」で考えずに済むように書こう
いつもお世話になってるkazzさんが「Rubyプログラマーは明示的なtrue/falseを意識するべきではないヨ」と指摘してくれました。
言い換えれば、Rubyでは条件分岐をナマのtrue/falseで考えるのではなく、条件そのもので考えようよ、ということですね😋。上で!!がRuboCopに怒られるのも、わざわざtrue/falseに還元するでないということでしょうね。
ビジネスロジックをいちいち原始的なtrue/falseに変換するのではなく、たとえば#paid?のように条件そのものをメソッド化する方が可読性も上がります。
if goods.paid == true # 残念な書き方
...
if goods.paid? # Rubyらしい書き方
...
本題: Rubocopで== trueや== falseが怒られない理由
しかし、単純に== trueや== falseを書く人をひっ捕らえてお尻をぺんぺんすればいいというものではなさそうです。
== trueや== falseという残念なはずの書き方は、意外にもRuboCopに怒られません。以下は素のRuboCopでエラーなしとなります。
# frozen_string_literal: true
a = false
'false' if a == false
追記(2022/10/20)
上の結果はRuby 3.1.2とRuboCop 1.36.0でも変わっていません。
Rubyスタイルガイド「2-61【統一】比較条件ではeven?やzero?などの述語メソッドが望ましい(==の直接使用は避ける」では、== nilといった書き方は望ましくないという記述はありますが、== trueや== falseが望ましくないとは記載されていません。
推測ですが、RuboCop奉行の立場としては以下のような理由があるのではないでしょうか。
nil?と違って、true?やfalse?がRubyにないので、それらを使えと単純にテンプレメッセージで警告しにくい- 外部API様のご都合などで
== trueや== falseと書くしかない状況がそこそこある
仮にお奉行様が== trueや== falseを無慈悲にビシビシ取り締まったら、それを回避するためにラッパーを書くなどの手間の方が大きくなり、あちこちで一揆の火の手があがるかもしれません。
落とし所としては、== trueや== falseを絶対悪として根絶するほどではないにしても、この書き方をするしかなかったときに「世間に負けた」と軽く敗北感を覚える方がよさそうです。「書いたらダメ」より「書いたら負け」の精神で。
おまけ: Pythonの場合
ついでにPythonのスタイルガイドを覗いてみると、== Trueや== Falseと書いてはいけないとありました。これも二値論理を壊さないためと思われますが、言語ではなくスタイルガイドでの制約ですね。
Yes:
if greeting:
No:if greeting == True:
Worse:if greeting is True:
同スタイルガイドより
なお、以下のようにPythonでは以下がすべてfalseになります。このあたりはRubyとはお家の事情が異なるということで。
- 偽であると定義されている定数:
NoneとFalse - 数値型におけるゼロ:
0,0.0,0j,Decimal(0),Fraction(0, 1) - 空のシーケンスまたはコレクション:
'',(),[],{},set(),range(0)
参考: Python ドキュメント「偽であると定義されている定数」
おたより発掘
Rubocop に !! が怒られるのが気に食わなかったけど、この記事読んで少しだけ納得した。
Ruby: Rubocopで`== true`や`== false`が怒られない理由 https://t.co/x8xxmKY3mu
— yuki.kakihara (@KakkiiiiKyg) February 16, 2019
更新情報