帮我理解这段Scala代码:scalaz IO Monad

4

这是我想要理解的代码(它来自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))

让我们从bufferFile的定义开始。我是否正确地认为io.IO的apply方法被调用了?那个apply方法接受一个返回值的无参函数(是吗?)。我猜这就是我卡住的地方。有人能解释一下bufferFile的定义是如何工作的吗?

2个回答

5

是的,你说得对,差不多;io.IO.apply会被一个所谓的“按名称”参数调用,它基本上是一个函数,接受一个没有参数(Unit)并返回A的函数。这个很酷的事情在于,当你直接传递A的实例,比如new BufferedReader(new FileReader(f)),它将被转换为类似于() => new BufferedReader(new FileReader(f))的东西。

由于apply的结果,你会得到一个IO[BufferedReader]的实例,它定义了一个def unsafePerformIO方法,它简单地返回捕获的BufferedReader实例。


2

补充 agilesteel回答,代码如下:

def bufferFile(f: File) = IO {   new BufferedReader(new FileReader(f)) }

等同于

def bufferFile(f: File) = new IO[A] {
  def unsafePerformIO = { new BufferedReader(new FileReader(f)) }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接