[WIP] Swiftでguardを条件分岐に使うと複雑になるという人の気持ちがわかった気がする
導入
Swiftのguardが出たときの雑談。私が知り合いに
私「guardってカッコ内returnのための条件を反転させてるから読みづらいよねー」
友人「条件を反転してるって考えるから難しいんですよ。あれは反転ではなくreturnしない方の処理の前提条件を書いてると考えたほうがいいですよ」
というふうに教えてくれた人が居た。そのときなるほどねー、いちいち反転するからややこしいんだと気づいて、つまり頭の中で次の図のように考えたら良いんだなと気がついてだいぶ楽になったんですね。
ただ、それでも読みづらいコードはあって、「いや、でも実際こういうコード読みづらくない?」と話してみると「それはそもそも単純に条件として読みづらいから」という話になったんですな。guard自体のややこしさとは別に単純に条件としてのややこしさを単純化しないと話が難しくなる。そのため、この記事ではそれについて書いてみます。
最初にこの記事の結論
- guardは反転していると考えず前提条件を書いているという気持ち
- guardの話が出てきたときに、もし、ifの機能がどうとかunlessだとか反転だとかというワードが出てきたら考えが違うので一致することなく平行線をたどる
具体例
たとえば次のようなguardがあるとして
guard 会計後にメール飛ばして良いかのフラグ else { return }// メール飛ばす処理が書いてある
上のコードでは次のように考えてしまうとややこしい
- メール飛ばしてよいかのフラグがありtrueを反転してfalseならreturnする
反転とは考えないということは次のように考えたほうが楽
- メール飛ばしてよいかどうか、という前提条件が書かれている
- 前提条件がtrueであるならその処理が続けられる
フラグが1つだと何を言ってるんだという気持ちになるので、二つにしてみる。
guard 会計後にメール飛ばしてよいかのフラグ && メールアドレス確認済み else {
return
}
// メール飛ばす処理が書いてある
上のコードでは反転とは考えない
- メール飛ばしてよいかのフラグがtrueでかつメールアドレスの確認済みもtrueなんだから処理を続ける前提処理が書いてある
- 前提条件を満たすなら、処理を続ける
それぞれのフラグが、trueでかつfalseのときreturnするなどは考えない。
まだ良い例が思いつかないが、4つぐらいあるとする
guard a && b && c && d else { return }
これを見て思うことは
- 前提条件は全てがtrueの場合
returnするために反転するとか考えてはいけない。
条件を変えてもう少し考えると
guard (a && b) || (c && d) else { return }
これでも、
- 処理を続ける前提条件は aとbがtrueだったら処理続けるし、または、cとdがtrueでも処理を続ける
なので気にならない。
とにかくreturnする処理のための条件だと考えない。
条件をシンプルに表現する
条件が次のような場合はそもそも条件が厳しい。guardが悪いわけじゃない
guard (!a && b) || (!c && d) else { return }
そもそも条件が複雑なので次のようなプロパティにしてみる
var メールを届けてもよいか: Bool { (!a && b) || (!c && d) }
そしてそれを前提条件と考える
guard メールを届けてもよいか else { return }// 以降メールを届ける処理がかいてある
前提という言葉を使ってきたけど、guardは前提条件に合わなければ防御するよ、という事なんだろうと考えるとguard 条件 else { return }の文法は次のように解釈できる。
// guard 条件 else { return }↓
// 前提条件に合わなければ防御するよ 条件 条件に合わない場合はどうするか {
return
}
なので、ifとかunlessとか反転なんかは気にすることはない
まとめ
- guardは反転していると考えず前提条件を書いているという気持ち
- guardの話が出てきたときに、もし、ifの機能がどうとかunlessだとか反転だとかというワードが出てきたら考えが違うので一致することなく平行線をたどる