这是对这个问题的跟进。
这是我试图理解的代码(来自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,所以它需要定义pure
和bind
抽象scalaz.Monad
特征的值。
pure
获取一个值并将其转换为包含在“容器”类型中的值。例如,它可能需要一个Int
并返回一个List[Int]
。这看起来很简单。
bind
接受一个“容器”类型和一个将容器持有的类型映射到另一种类型的函数。返回的值是相同的容器类型,但它现在拥有一个新类型。一个示例是使用将s 映射到s的函数将aList[Int]
映射到 a 。和差不多吗?List[String]
Int
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
解析为容器类型?