Это продолжение этого вопроса.
Вот код, который я пытаюсь понять (он взят из 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))
Теперь я пытаюсь понять неявный неявный val IOMonad
определение. Вот как я это понимаю. Это scalaz.Monad , поэтому ему необходимо определить pure
и привязать
абстрактные значения черты scalaz.Monad
.
pure
принимает значение и превращает его в значение, содержащееся в типе «контейнер». Например, он может взять Int
и вернуть List [Int]
. Это кажется довольно простым.
bind
принимает тип «контейнера» и функцию, которая сопоставляет тип, который хранится в контейнере, с другим типом. Возвращаемое значение является контейнером того же типа, но теперь содержит новый тип. В качестве примера можно взять List [Int]
и сопоставить его с List [String]
с помощью функции, которая отображает Int
s в 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)()
}
}
, я думаю?
метод неявно
ищет неявное значение (значение, верно?) который реализует Монаду [Function0]
. Откуда взялось это неявное определение? Я предполагаю, что это из определения implicit val IOMonad = new Monad [IO] {...}
, но мы сейчас внутри этого определения, и все становится немного круговым, и мой мозг начинает застряли в бесконечном цикле :)
Кроме того, первый аргумент bind
( () => a.unsafePerformIO
) кажется функцией, которая не принимает параметров и возвращает a.unsafePerformIO. Как мне это прочитать? bind
принимает тип контейнера в качестве первого аргумента, поэтому, возможно, () => a.unsafePerformIO
преобразуется в тип контейнера?