Scalaのガード
近年においてガードが用いられるコードをよく見るようになりました。Scalaにおいてガードのモダンな書き方はどう書くのだろうと思ったので、纏めます。
ガードとは
ガードとは、処理の分岐においてtrueと評価し処理を継続させる条件を表す式であり、trueでなかった場合は別途例外処理を行うものです。
注意点として、関数の始まりのうちからパラメータに例外となりうる値が含まれている場合などにおいて、早めにreturnさせることでネストを減らすことができる早期リターンもまた、ガードを使いますが、ガード = 早期リターン ではありません。
scalaでガードを使う場合、シンプルに if と return の組み合わせで実現することが出来ます。
def hoo(bar: Int): Boolean = {
if (bar < 0) return false
true
}
hoo(-1)
ガードがある言語
ガードはif と returnで実現できますが、Swift等の言語には言語としてガードとしての機能があり、guard
を用いて、不正な値の場合は、本来の処理を抜けて例外処理に移行することが出来ます。ここまでは通常のifでも可能ですが、Swiftの場合は、必ずreturnを書かなければコンパイルが通らないようになっています。また、String?
は、オプショナル型なので、アンラップする必要がありますが、guard let
でそのままアンラップの役割を果たすようになっています。
func hoo(bar: String?) {
guard let baz = bar else {
print("no string")
return
}
print(baz)
}
hoo(bar: "string")
hoo(bar: nil)
パターンガード
if と return を使ったガードの他にガードの機能を有している式があります。
matchを使うパターンガード
def hoo(baz: Int) = baz match {
case i: Int if i < 0 => false
case _ => true
}
hoo(-1) // false
hoo(0) // true
hoo(1) // true
matchを使った場合は、上から順番に処理されていくため、パターンに応じてガードを書いていくことが可能で、ifとは異なり型のマッチングも簡潔に書くことができます。
for を使ったパターンガード
forのジェネレーターでifを使うことで、条件に当てはまらない場合は、for全体で None が返ります。
例はざっくりなので、Some(true) と None が返っているので getOrElse でbooleanに直しています。
def hoo(baz: Int) = {
(for {
_ <- Some(baz)
if baz >= 0
} yield {
true
}).getOrElse(false)
}
hoo(-1) // false
hoo(0) // true
hoo(1) // true
最後に
以上でScalaにおけるガードについて纏めた記事になります。Scalaでガードを使う場面は、scala.collection.Traversableのメソッドによって解決できることでもあるので、ガードを使うことがベストというわけではありません。
Scalaでガードはどうやるのかな?と疑問を解決するために纏めました。何かの解決になれば幸いです。
追記
scalamacrosを使いguard節を実装する例を書きました。
ScalaでRustっぽく書きたい 〜try!編〜