型クラス(Type Class)の練習
プロパー型
まずはプロパー型(proper type)と呼ばれる普通のヤツ。ここではIntとStringに対応した例。
implicitの宣言として、valでもobjectでもいけるので2つ書いておいた。
object Main extends App {
def decorate[T](x: T)(implicit deco: Decorating[T]) = deco(x)
trait Decorating[T] {
def apply(x: T): String
}
implicit val DecoratingString = new Decorating[String] {
def apply(x: String) = s"SSS $x SSS"
}
implicit object DecoratingInt extends Decorating[Int] {
def apply(x: Int) = s"III $x III"
}
println(decorate(100)) // III 100 III
println(decorate("hello")) // SSS hello SSS
}
型コンストラクタ
次に1階カインド型(1st-order-kinded type)とか型コンストラクタ(type constructor)とか呼ばれる型パラメータを取るヤツ。
ここではOptionを使う。
以下、Option[String]に対応した例。
Option[Int]も同じようにやるだけなので省略。
implicit object OptionString extends Decorating[Option[String]] {
def apply(x: Option[String]) = x.fold("")("OOO SSS " + (_:String) + " SSS OOO")
}
println(decorate(Option("hello"))) // OOO SSS hello SSS OOO
組み合わせ
上記でも出来るのだが、でも待って欲しい。
Decorating[String]も宣言しているのにDecorating[Option[String]]ではほぼ同じようなコードを書いてしまっている。
Decorating[Option[String]]からDecorating[String]のコードを呼び出して再利用したいはず。
こうやれば出来る。
これでStringにもIntにも対応できている。
implicit def DocoratingOption[T](implicit d: Decorating[T]) = new Decorating[Option[T]] {
def apply(x: Option[T]) = x.fold("")("OOO " + d(_: T) + " OOO")
}
println(decorate(Option("hello"))) // OOO SSS hello SSS OOO
println(decorate(Option(9))) // OOO III 9 III OOO
println(decorate(Option.empty[Int])) // 何も表示されない
Option[T]のT部分を動的にしないといけないのでimplicit defで実現している。
プログラム全体
最後に今回のプログラムを貼り付けておきます。
object Main extends App {
def decorate[T](x: T)(implicit deco: Decorating[T]) = deco(x)
trait Decorating[T] {
def apply(x: T): String
}
implicit val DecoratingString = new Decorating[String] {
def apply(x: String) = s"SSS $x SSS"
}
implicit object DecoratingInt extends Decorating[Int] {
def apply(x: Int) = s"III $x III"
}
implicit def DocoratingOption[T](implicit d: Decorating[T]) = new Decorating[Option[T]] {
def apply(x: Option[T]) = x.fold("")("OOO " + d(_: T) + " OOO")
}
println(decorate("hello"))
println(decorate(9))
println(decorate(Option("hello")))
println(decorate(Option(9)))
println(decorate(Option.empty[Int]))
}