ここらへんのエントリ読んだんだけど、いまいち分からなかったのでscalazのコード読んだ。のをまとめる。
OptionとFunctorに絞って調べたけど、多分それ以外も同じだと思う。
実行環境はscala v2.10.3, scalaz v7.0.6。偶然手元でIntelliJで開いてたプロジェクトのバージョンがそうだっただけで特にこのバージョン特有の何かがあるわけではない。
なぜsome(1)がSome(1)になるのか
import scalaz._
import Scalaz._
some(1) // Some(1)
定義元を調べると、ここ
final def some[A](a: A): Option[A] = Some(a)
これは単純にimport Scalaz._
した時に
package scalaz
object Scalaz
extends StateFunctions // Functions related to the state monad
with syntax.ToTypeClassOps // syntax associated with type classes
with syntax.ToDataOps // syntax associated with Scalaz data structures
with std.AllInstances // Type class instances for the standard library types
with std.AllFunctions // Functions related to standard library types
with syntax.std.ToAllStdOps // syntax associated with standard library types
with IdInstances // Identity type and instances
ここがimportされ、std.AllFunctions→OptionFunctionsとたどるとsome
の定義元にたどり着く。
このようなXxxFunctions
traitはグローバル関数っぽく使う関数が定義されているらしい。
1.someがなぜSome(1)になるのか。
import scalaz._
import Scalaz._
1.some // Some(1)
これの仕組み。
この.some
の定義元をIDEで調べると、ここになる
trait OptionIdOps[A] extends Ops[A] {
def some: Option[A] = Some(self)
}
なぜこれが呼び出されるのか?
前節と同じようにScalaz→syntax.std.ToAllStdOps→ToOptionIdOpsの順でたどると
trait ToOptionIdOps {
implicit def ToOptionIdOps[A](a: A) = new OptionIdOps[A] { def self = a }
}
ここにimplicit conversionが定義されており、1
がnew OptionIdOps[Int] { def self = 1 }
に変換され、このオブジェクトがsome
メソッドを持ってるのでSome(1)
になる。
このようなXxxIdOps
はimplicit conversionを利用して既存の型に生やすメソッドを定義している模様1。ToXxxIdOps
がそのとき使うimplicit conversionを定義してる。多分OpsはOperationsの略。
なぜFunctor[Option].fpair(Option(1))がSome(1,1)になるのか
ここからが本番
import scalaz._
import Scalaz._
Functor[Option].fpair(Option(1)) // Some(1,1)
Functor[Option]
は
object Functor {
@inline def apply[F[_]](implicit F: Functor[F]): Functor[F] = F
////
////
}
これのapply
とimplicitパラメータを省略した形2。なので、F
が返る。このF
はFunctor[Option]
型のimplicitパラメータで、
implicit val optionInstance = new Traverse[Option] with MonadPlus[Option] with Each[Option] with Index[Option] with Length[Option] with Cozip[Option] with Zip[Option] with Unzip[Option] with IsEmpty[Option] {
略
これ。Traverse
がFunctor
をmix-inしているので、optionInstance
がFunctor[Option]
になる。これは今まで同様Scalaz→std.AllInstances→OptionInstancesの順にたどると辿り着く。
よって、Functor[Option].fpair(Option(1))
はoptionInstance.fpair(Option(1))
になる。
で、Functor traitに定義されているfpair
def fpair[A](fa: F[A]): F[(A, A)] = map(fa)(a => (a, a))
が呼び出され、Option(1)
を渡すとSome(1,1)
になる。
なぜimplicitly[Functor[Option]].fpair(Option(1))がSome(1,1)になるのか
ここらへんを調べると、以下の2つの書き方が出てくる。
Functor[Option].fpair(Option(1))
implicitly[Functor[Option]].fpair(Option(1))
これはどちらも前節で出てきたoptionsInstance
を取得しているだけ。前者のほうはapply
の第一引数リストが(implicit F: Functor[F])
になっていてoptionInstance
を取得していて、後者はこのimplicit parameterを直接取得している3。あとは前節と同じ仕組み。
なぜOption(1).fpairはSome(1,1)になるのか
import scalaz._
import Scalaz._
Option(1).fpair // Some(1,1)
Scalaz→ToTypeClassOps→ToFunctorOpsでたどると
implicit def ToFunctorOps[F[_],A](v: F[A])(implicit F0: Functor[F]) =
new FunctorOps[F,A] { def self = v; implicit def F: Functor[F] = F0 }
このimplicit conversionが定義されている。今、F
はOption
, A
はInt
だ。
で、implicit F0: Functor[F]
は既に見たとおりoptionInstance
だ。
なので、Option(1).fpair
はnew FunctorOps[Option,Int] { def self = Option(1); implicit def F: Functor[Option] = optionInstance }.fpair
になる。
で、fpair
の定義は
final def fpair: F[(A, A)] = F.fpair(self)
になっていて、つまりoptionInstance.fpair(Option(1))
となる。これはもはや前節・前前節と同様なわけで、Some(1,1)になる。
このようにToXxxOps
はXxxOps
へのimplicit conversionを定義している模様。
型クラス
前節の仕組みはFunctor+Optionだったが、例えばFunctor+Listでも同様のことが出来る。
import scalaz._
import Scalaz._
List(1).fpair // List((1,1))
また、この時実行されるFunctorまわりのコードはOptionの時と同じだ。違うものは、Optionの時optionInstanceだったのがlistInstance
になるだけだ。
implicit val listInstance = new Traverse[List] with MonadPlus[List] with Each[List] with Index[List] with Length[List] with Zip[List] with Unzip[List] with IsEmpty[List] {
略
fpair
のインタフェースを変えずに複数の型で動くようになっている。このようにimplicit conversionを駆使して多相性を実現するテクニックは、Haskellの型クラスの機能をエミュレートしているためscalaでも単に型クラスと言うらしい。
以下のページが分かりやすかった。
参考
- Scalaz の Syntax という仕組みの解説 - scalaとか・・・
- ひなたねこ: Scalazのかきかた
- 独習 Scalaz — 13日目 (import ガイド)
- こわくない型クラス
- 型クラスペディア(The Typeclassopedia)