前置き: 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
更新情報