今回は時間がないので簡単に見ます。
型クラスによってアドホック多相が実現されるとどんな柔軟なことができるのかという例を見ましょう。
たとえば、List#max について見て見ましょう。これ、要素が String だったら辞書順での最大値を返すし、Int だったら数的な意味での最大値を返しますよね。もし「そもそも比べられない型」が要素に入ってたらコンパイルエラーになります。
class Nyan(val value:String)//比べることのできない型println(List(1,2,3).max)// => 3println(List("a","b","c").max)// => cprintln(List(new Nyan("a"),new Nyan("b"),new Nyan("c")).max)// => error:No implicit Ordering defined for this.Nyan.
おっこれはまさにアドホック多相ですね。そして、API ドキュメントを読むと、これのFull Signature はdef max[B >: A](implicit cmp: Ordering[B]): A になっています。あっこれは完全に型クラスですね。
では例によって動きを詳しく見てみましょう。Aはどこから出てきたかというと List の定義class List[+A] からです。となると、B は、[B >: A] より、Listの要素と同じクラスか、その親クラスに限定されることになりますね。ではこのときList(1, 2, 3).max とすると、何が起こるでしょうか。まず A が Int に確定しますね。これによって、B は Int かその親クラスに確定します。すると implicit cmp の型は Ordering[B :> Int]となりますね。APIドキュメントを引くとIntOrdering extends Ordering[Int] という trait が見つかりました。これで implicit cmp の型は IntOrdering に確定です。ここまでは今までさんざん見てきたのと同じ理屈ですね。
では新しく作った Nyan を Ordering 型クラスのインスタンスにしてみましょう。Ordering[Nyan] 型の implicit な値を定義してあげればよかったですね。
class Nyan(val value:String) {override def toString = s"Nyan(${value})"// 表示のため}implicitval nyanOrdering =new Ordering[Nyan] { def compare(a: Nyan, b:Nyan) = implicitly[Ordering[String]].compare(a.value, b.value)}println(List(1,2,3).max)// => 3println(List("a","b","c").max)// => cprintln(List(new Nyan("a"),new Nyan("b"),new Nyan("c")).max)// => Nyan(c)
おおー!!List#maxがNyanに対応した!!!!型クラス、めっちゃ便利なのでは?ということがわかってきたかと思います。
あっ、しれっとimplicitly[Ordering[String]] とかいう今まで見たことのない新要素が登場していますが、これは「context bound」と呼ばれる記法で、「Ordering[String] 型の implicit な値を探してきてここに入れてください」という意味です。今回は Nyan の中の値が文字列なので、Ordering[String] の compareメソッドに処理を委譲したかったわけですが、特別なことはなにもせずにList("a", "b", "c").maxが呼べるということは、すでに Ordering[String] 型の implicit な値はこのスコープに存在しているわけで、だったらそれ使っちゃえばいいよね、ということでここで context bound を活用してみました。今APIリファレンス改めて引いたらOrdering.String ってのがあったので、Ordering.String.compare でもかまわないと思います。
引用をストックしました
引用するにはまずログインしてください
引用をストックできませんでした。再度お試しください
限定公開記事のため引用できません。