Esta es una continuación de la pregunta this.Ayúdeme a entender este código de Scala: Scalaz IO Monad e implica
Aquí está el código que estoy tratando de entender (que es de 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)()
}
}
}
Este código se utiliza como esto (estoy asumiendo un import io._
está implícito)
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 Ahora estoy tratando de entender la definición implicit val IOMonad
. Así es como lo entiendo. Esto es scalaz.Monad, por lo que necesita definir pure
y bind
valores abstractos del rasgo scalaz.Monad
.
pure
toma un valor y lo convierte en un valor contenido en el tipo "contenedor". Por ejemplo, podría tomar un Int
y devolver un List[Int]
. Esto parece bastante simple.
bind
toma un tipo "contenedor" y una función que asigna el tipo que el contenedor mantiene a otro tipo. El valor que se devuelve es el mismo tipo de contenedor, pero ahora contiene un nuevo tipo. Un ejemplo sería tomar un List[Int]
y asignarlo a List[String]
usando una función que asigna Int
s a String
s. ¿Es bind
prácticamente lo mismo que map
?
La implementación de bind
es donde estoy atascado. Aquí está el código:
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)()
}
Esta definición tiene IO[A]
y lo asigna a IO[B]
utilizando una función que toma un A
y devuelve un IO[B]
. Supongo que para hacer esto, tiene que usar flatMap
para "aplanar" el resultado (¿correcto?).
El = IO { ... }
es lo mismo que
= new IO[A] {
def unsafePerformIO = implicitly[Monad[Function0]].bind(() => a.unsafePerformIO,
(x:A) =>() => f(x).unsafePerformIO)()
}
}
que pienso?
el método implicitly
busca un valor implícito (valor, ¿no?) Que implementa Monad[Function0]
. ¿De dónde viene esta definición implícita? Supongo que esto es de la definición implicit val IOMonad = new Monad[IO] {...}
, pero estamos dentro de esa definición ahora y las cosas se vuelven un poco circulares y mi cerebro comienza a atascarse en un bucle infinito :)
Además, el primer argumento para bind
(() => a.unsafePerformIO
) parece ser una función que no toma parámetros y devuelve a.unsafePerformIO. ¿Cómo debería leer esto? bind
toma un tipo de contenedor como primer argumento, por lo que ¿() => a.unsafePerformIO
se resuelve en un tipo de contenedor?
Scalaz en realidad ofrece una mónada IO fuera de la caja ahora. import scalaz.efectos._ – Apocalisp