scala.PartialFunction condOpt を使おう

というわけで、なんとなくたまに欲しくなっていた気もするけど、今までほぼ使ったことなかったPartialFunctionのcondOptについて使い方を説明してみます。

https://github.com/scala/scala/blob/v2.11.6/src/library/scala/PartialFunction.scala#L276-L286


使い道は、以下のようなパターン(play 2.1.0 のコードより)
https://github.com/playframework/Play20/blob/2.1.0/framework/src/play/src/main/scala/play/api/i18n/Messages.scala#L63-L73

code match {
  case SimpleLocale(language) => Some(Lang(language, ""))
  case CountryLocale(language, country) => Some(Lang(language, country))
  case _ => None
}

ポイントは

  • match式を使っていて
  • そのmatch式の戻り値の型がOption
  • 先頭からmatchさせていったとき、一番最後以外のmatchした場合については、明示的にSomeに包んで返している
  • なので最後は case _ => None というのがくる

つまりモチベーションとしては

  • 毎回自ら明示的にSomeに包むの面倒
  • case _ => Noneという定形コードうざい

でそれらをPartialFunction使って短く書けるものが、PartialFunctionのコンパニオンオブジェクトにcondOptという名前で標準で存在します。
さっきのをcondOptで書き換えると以下のようになります

PartialFunction.condOpt(code){
  case SimpleLocale(language) => Lang(language, "")
  case CountryLocale(language, country) => Lang(language, country)
}

はい、ただこれだけの違いです。ちなみに、パフォーマンス的にはAbstractPartialFunctionのオブジェクトのインスタンスなど*1が作られる分だけ、match式よりこっちのほうが微妙にオーバーヘッドがあります。(まぁこんな細かいパフォーマンスが問題になることは、ほぼないでしょうけれども)

*1: 内部でlift呼んでいるので、ほかにも作られるみたい https://github.com/scala/scala/blob/v2.10.1-RC1/src/library/scala/PartialFunction.scala#L214-L221