Scala

Scalaのガード

More than 1 year has passed since last update.

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!編〜