以下是我试图理解的代码(来自http://apocalisp.wordpress.com/2010/10/17/scalaz-tutorial-enumeration-based-io-with-iteratees/):
object io {
sealed trait IO[A] {
def unsafePerformIO: A
}
object IO {
def apply[A](a: => A): IO[A] = new IO[A] {
def unsafePerformIO = a
}
}
implicit val IOMonad = new Monad[IO] {
def pure[A](a: => A): IO[A] = IO(a)
def bind[A,B](a: IO[A], f: A => IO[B]): IO[B] = IO {
implicitly[Monad[Function0]].bind(() => a.unsafePerformIO,
(x:A) => () => f(x).unsafePerformIO)()
}
}
}
这段代码的使用方法如下(我假设已经暗含了
import io._
):def bufferFile(f: File) = IO { new BufferedReader(new FileReader(f)) }
def closeReader(r: Reader) = IO { r.close }
def bracket[A,B,C](init: IO[A], fin: A => IO[B], body: A => IO[C]): IO[C] = for { a <- init
c <- body(a)
_ <- fin(a) } yield c
def enumFile[A](f: File, i: IterV[String, A]): IO[IterV[String, A]] = bracket(bufferFile(f),
closeReader(_:BufferedReader),
enumReader(_:BufferedReader, i))
我现在正在尝试理解
implicit val IOMonad
的定义。这是一个 scalaz.Monad,因此需要定义 scalaz.Monad
特质的 pure
和 bind
抽象值。
pure
接受一个值并将其转换为包含在 "容器" 类型中的值。例如,它可以将一个 Int
转换为一个 List[Int]
。这似乎非常简单。
bind
接受一个 "容器" 类型和一个将容器所持有的类型映射到另一种类型的函数。返回的值是相同的容器类型,但现在它持有一个新类型。例如,使用将 Int
映射到 String
的函数,可以将 List[Int]
映射到 List[String]
。那么,bind
是否与 map
大致相同呢?
bind
的实现是我卡住的地方。这是代码:
def bind[A,B](a: IO[A], f: A => IO[B]): IO[B] = IO {
implicitly[Monad[Function0]].bind(() => a.unsafePerformIO,
(x:A) => () => f(x).unsafePerformIO)()
}
这个定义接受一个类型为
IO[A]
的参数,并使用一个函数将其映射到类型为IO[B]
的结果。该函数接受一个类型为A
的参数并返回一个类型为IO[B]
的结果。我猜想,为了实现这一点,它必须使用flatMap
来“展平”结果(对吗?)。
= IO { ... }
与以下表达式相同: = new IO[A] {
def unsafePerformIO = implicitly[Monad[Function0]].bind(() => a.unsafePerformIO,
(x:A) => () => f(x).unsafePerformIO)()
}
}
我认为?
implicitly
方法查找实现了 Monad[Function0]
的隐式值(值,对吗?)。这个隐式定义是从哪里来的?我猜这是从 implicit val IOMonad = new Monad[IO] {...}
定义中来的,但我们现在正在该定义内部,事情有点循环,我的大脑开始陷入无限循环 :)
另外,bind
的第一个参数 (() => a.unsafePerformIO
) 似乎是一个不带参数并返回 a.unsafePerformIO
的函数。我该如何阅读这个?bind
将容器类型作为其第一个参数,因此也许 () => a.unsafePerformIO
解析为容器类型?