Help me understand this Scala code: scalaz IO Monad and implicits
문제
This is a followup to this question.
Here's the code I'm trying to understand (it's from 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)()
}
}
}
This code is used like this (I'm assuming an import io._
is implied)
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))
I'm now trying to understand the implicit val IOMonad
definition. Here's how I understand it. This is a scalaz.Monad, so it needs to define pure
and bind
abstract values of the scalaz.Monad
trait.
pure
takes a value and turns it into a value contained in the "container" type. For example it could take an Int
and return a List[Int]
. This seems pretty simple.
bind
takes a "container" type and a function that maps the type that the container holds to another type. The value that is returned is the same container type, but it's now holding a new type. An example would be taking a List[Int]
and mapping it to a List[String]
using a function that maps Int
s to String
s. Is bind
pretty much the same as map
?
The implementation of bind
is where I'm stuck. Here's the code:
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)()
}
This definition takes IO[A]
and maps it to IO[B]
using a function that takes an A
and returns an IO[B]
. I guess to do this, it has to use flatMap
to "flatten" the result (correct?).
The = IO { ... }
is the same as
= new IO[A] {
def unsafePerformIO = implicitly[Monad[Function0]].bind(() => a.unsafePerformIO,
(x:A) => () => f(x).unsafePerformIO)()
}
}
I think?
the implicitly
method looks for an implicit value (value, right?) that implements Monad[Function0]
. Where does this implicit definition come from? I'm guessing this is from the implicit val IOMonad = new Monad[IO] {...}
definition, but we're inside that definition right now and things get a little circular and my brain starts to get stuck in an infinite loop :)
Also, the first argument to bind
(() => a.unsafePerformIO
) seems to be a function that takes no parameters and returns a.unsafePerformIO. How should I read this? bind
takes a container type as its first argument, so maybe () => a.unsafePerformIO
resolves to a container type?
올바른 솔루션이 없습니다