kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

ひょっとしたら役に立つかもしれないScala Tips(4) - パターンの漏れを検出する

ScalaでMLやHaskellのalgebraic data typeを模倣するときの定番は、abstract class(or trait)とcase classを使って、

trait Exp
case class Add(l: Exp, r: Exp) extends Exp
case class Num(v: Int) extends Exp

のようにすることだが、非常に残念なことに、これでは、MLなどと違って、パターンマッチ時にパターンの漏れがあってもコンパイル時にそれを検出することができない。たとえば、

def eval(e: Exp): Int = e match {
  case Add(l, r) => eval(l) + eval(r)
  //case Num(...)が抜けてる!
}

のようなコードを書いても、コンパイラは警告を出すことなく、黙って受け入れてしまう。このようなことを防ぎ、MLなどと同様に警告を出すようにするには、sealedを使って、

sealed trait Exp
case class Add(l: Exp, r: Exp) extends Exp
case class Num(v: Int) extends Exp

とすれば良い。このようにすれば、

def eval(e: Exp): Int = e match {
  case Add(l, r) => eval(l) + eval(r)
  //case Num(...)が抜けてる!
}

のような関数を書くと、コンパイラ

<console>:8: warning: match is not exhaustive!
missing combination            Num

       def eval(e: Exp): Int = e match {

のように書き漏らしたパターンを報告してくれるようになる。ちなみに、標準ライブラリのOption型等はsealedを使って書かれているため、書き漏らしたパターンがある場合、ちゃんとコンパイラがそのことを報告してくれる。