読者です 読者をやめる 読者になる 読者になる

Scala で直和型

ScalaMatsuriに参加したからというわけではありませんがScalaの話題を。

まず最初に直和型とは

例えばOptionみたいなやつです。Optionは、「必ずSome か Noneのどちらか」であるような型ですが、こういう「必ずAかB(かCか……)である」というような型を直和型と言います。

では、自分で直和型を定義するときのことを考えてみましょう。典型的かつ素直なアイデアとしては、 sealed trait を利用する方法があります。

sealed trait FirstPrecure
case object CureBlack extends FirstPrecure
case object CureWhite extends FirstPrecure

さて、これで FirstPrecure な値は CureBlack か CureWhite のどらかであるというのが表現できましたね。ポイントは trait を sealed で宣言している部分で、これによって外のファイルから勝手にFirstPrecureであるような型を追加することができなくなっています。

典型的なオブジェクト指向において、直和型というのは継承によって表現することが可能である、と言うことができるでしょう。

さて、ここまでは CureBlack も CureWhite も自分で定義した型なので単純な話なのですが、この方法だと「Int と String の直和型を作りたい」とか「自分が定義した型じゃないものの直和型を作りたい」みたいなときに困ってしまいます。自分が作ったわけではない型を勝手に自分が作った型の子クラスにすることはできませんからね。サブタイピングによる直和型表現の限界です。

型クラスによる表現を考えてみる

サブタイピングで表現できない、となったときの発想として、「じゃあそれ型クラスで表現できないかなぁ」という発想がありえそうです。素直にやるとこうかな?

sealed trait IntOrString[A]
implicit val i = new IntOrString[Int]{}
implicit val s = new IntOrString[String]{}

def nyan[T:IntOrString](x: T) = x

println(nyan(1)) // => 1
println(nyan("hoge")) // => hoge
println(nyan('c')) // => compile error

うーん、まあ、やりたいことが実現できていると言えばできていますね(implicit parameterが型クラスと同等であるという話に関しては 以下のエントリを参照してください Scala の implicit parameter は型クラスの一種とはどういうことなのか - 猫型の蓄音機は 1 分間に 45 回にゃあと鳴く)。しかしちょっと直感的ではない気もするし、val i とか val s とかが名前空間汚してる感じがある……。でもわたしのお脳ではこのアイディアまでが限界でした。

というわけで調べてみた

私よりも頭の良いひとが絶対になんか良い方法を考えついているはず、と思い調べてみたら、やっぱりというかなんというか、もっときれいな方法で直和型を表現する方法が Scalaz で提供されているようです。see 独習 Scalaz — 余積

type IntOrString = t[String]#t[Int] という構文で直和が表現できているのは直感的(というかHaskellっぽい)ですし、なるほどなるほど〜という感じです。詳しいメカニズムについてはまだ理解しきれてないので今度じっくり読んで考えてみることにして、脳が疲れたのでひとまず今日はここまで。

というか、TaPL 積んでるの完全にアカンという感じになってきたな……。