Scala 2.9でパターンマッチでコンパイルエラー

Scalaはパターンマッチ内で正規表現を使ってマッチするかどうかの判定及びマッチした箇所の抽出が出来る。

   val regex0 = "(.*)hell".r
   val input = "nutshell"
   val result = input match {
      case regex0(p) => Some(p)
      case _ => None
   }
   assert(result == Some("nuts"))

これは便利なんだけど、調子に乗って沢山書きすぎると2.9系だとコンパイル時にエラーが出るようになる。

   val r1 = "(\\d+)個".r
   val r2 = "(\\d+)匹".r
   val r3 = "(\\d+)尾".r
   val r4 = "(\\d+)人".r
   val r5 = "第(\\d+)番".r
   val r6 = "バージョン(\\d+)".r
   val r7 = "(\\d+)円".r
   val r8 = "¥(\\d+)".r
   val r9 = "(\\d+)羽".r

   val input = "10円"
   val result = input match {
      case r1(n) => Some(n)
      case r2(n) => Some(n)
      case r3(n) => Some(n)
      case r4(n) => Some(n)
      case r5(n) => Some(n)
      case r6(n) => Some(n)
      case r7(n) => Some(n)
      case r8(n) => Some(n)      
      case r9(n) => Some(n)  
      case _ => None
   }
   assert(result == Some("10"))

まあ内容は再現用サンプルなんで特に意味はないです。
このコードは、こんなエラーが出てコンパイル出来ない。

[info] Compiling 1 Scala source to /tmp/ScalaSample/target/scala-2.9.1/classes...
[trace] Stack trace suppressed: run 'last compile:compile' for the full output.
[error] (compile:compile) java.lang.Error: ch.epfl.lamp.fjbg.JCode$OffsetTooBigException: offset too big to fit in 16 bits: 42287
[error] Total time: 15 s, completed 2013/06/15 4:58:17

どうやら既知の問題のようで、2.10では解消していて、ちゃんとコンパイル出来る。

どうしても2.9系を使い続けたい時には、match式を複数のPartialFunctionに分割してorElseで結合したらどうか。

   ... 
   val input = "10円"
   val cases1: PartialFunction[String, Option[String]] = {
      case r1(n) => Some(n)
      case r2(n) => Some(n)
      case r3(n) => Some(n)
      case r4(n) => Some(n)
      case r5(n) => Some(n)
   }
   val cases2: PartialFunction[String, Option[String]] = {
      case r6(n) => Some(n)
      case r7(n) => Some(n)
      case r8(n) => Some(n)      
      case r9(n) => Some(n)  
      case _ => None
   }
   val result = (cases1 orElse cases2) apply input

   assert(result == Some("10"))

コンパイルが通って実行も出来る。でもコンパイル時に網羅性のチェックは出来ないかも。

PartialFunctionを可変長引数で渡してreduceで合成するようにしておけば見た目きれいかも。

object Match {
   def forCases[A, B](a: A)(cases: PartialFunction[A, B]*): B = {
      cases.reduce(_ orElse _) apply a
   }
}

使う側

   val input = "10円"
   val result = Match.forCases(input)(
      { case r1(n) => Some(n) },
      { case r2(n) => Some(n) },
      { case r3(n) => Some(n) },
      { case r4(n) => Some(n) },
      { case r5(n) => Some(n) },
      { case r6(n) => Some(n) },
      { case r7(n) => Some(n) },
      { case r8(n) => Some(n) },
      { case r9(n) => Some(n) },
      { case _ => None}
   )

   assert(result == Some("10"))

まあ結局網羅性のチェックは出来ないので完全にはmatch式の代わりにはならないけど。

とっとと2.10に上げましょうということかな。