はじめに
僕はcase文だったりswitch文のelse(default)節に正常処理を書くことが嫌いです。
ただ、if文のelse節には正常処理をガンガン書きます。
どうして、そうしているのか、その訳をつらつらと書いていきたいと思います。
Rubyの制御構造(条件分岐)
Rubyの制御構文、特に条件分岐に関するものは以下の三種類がある。
- if文
- unless文
- case文
それぞれの構文は以下の通りだ。式を後置修飾するif修飾子およびunless修飾子については本題から逸れるので省略した。
if文
if 式1
処理1
elsif 式2
処理2
end
unless文
unless 式
処理1
else
処理2
end
case文
case 式0
when 式1
処理1
when 式2
処理2
else
処理3
end
if文はelsif節を使って、case文はそもそもの構文として複数条件による分岐ができる。それに対して、unless文にはそれに該当する構文がない。
そのため、case文とif文はお互いに書き換えができ、実際、rubyの日本語リファレンスにもcase文からif文への書き換え方が記載されている。
ただ、僕はcase文とif文にはその目的、使用意図に決定的な違いがあると考えている。それどころか、個人的にはif文とunless文が同じ枠組みで、case文はそれらとは別物だと思っている。
if文とcase文の目的の違いこそが、僕にcase文のelse節でraiseしたい衝動を呼び起こしているのだ。
if文とcase文の違い
端的に表せば、それは条件式に期待されることだ。簡単に書くと次の通りだ。
- if文:boolean
- case文:ある集合に属する値
if文の場合、構文的にも条件式の値はbooleanとして解釈されるし、読み手もそれを期待する。(少なくとも僕はそうだ)
もちろん、Rubyはfalseとnilだけをfalseとして解釈し、それ以外の値を全てtrueとして評価するから、if文の条件式にboolean以外を与えることはできるし、それについては、nilをundefinedのように扱って、特定の値が存在すれば処理をする、のような書き方もできるから否定はしない。というか、割と使う。
case文の場合、そもそも条件の成立条件はそれぞれの処理に紐づいている条件式だけでは決まらない。なぜなら、大元の式の評価値とそれぞれの条件式の評価値の一致を条件の成立としているからだ。
そのため、個人的にはcase文はある変数が特定の値の内どれかであることが確定している場合に使うことが多い。
例として、あまり綺麗ではないが以下のコードを掲載しておく。
module Brother
Taro = 1
Jiro = 2
Saburo = 3
end
def name(brother)
case brother
when Brother::Taro
'taro'
when Brother::Jiro
'jiro'
when Brother::Saburo
'saburo'
else
raise "#{name} is not a brother"
end
end
仮に、上の例のnameメソッドを以下のように書き換えると非常に気味が悪い。
なぜなら、変数brotherに兄弟ではないmichaelやjohnが来たとしても、saburoとして評価してしまうからだ。
実務的な理由から、raiseではなくロギングにとどめていることもあるが、それでもsaburoと返してしまうよりはマシだろう。
つまり、個人的にはcase文のelse節はそもそも限りなく例外に近いのだ。
契約を破った要求に対して、見当違いの応答をするぐらいなら拒否してくれたほうが、まだ良心的だ。
def name(brother)
case brother
when Brother::Taro
'taro'
when Brother::Jiro
'jiro'
else
'saburo'
end
end
最後に
なぜ、こんな考え方になったんだろう、と思い返すと、自分がシステムに埋め込んだあるバグと、攻撃的プログラミングという考え方、また、以前読んだあるエッセイに影響を受けていると思う。
参考までにそれぞれへのリンクを載せて終わることにする。