Questo è un seguito alla domanda this.Aiutami a capire questo codice Scala: scalaz IO Monade e impliciti
Ecco il codice che sto cercando di capire (è da 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)()
}
}
}
Questo codice viene utilizzato come questo (sto assumendo un import io._
è implicito)
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 ora sto cercando di capire la definizione implicit val IOMonad
. Ecco come lo capisco. Questo è un scalaz.Monad, quindi è necessario definire i valori astratti pure
e bind
del tratto scalaz.Monad
.
pure
prende un valore e lo trasforma in un valore contenuto nel tipo "contenitore". Ad esempio potrebbe richiedere un Int
e restituire un List[Int]
. Questo sembra piuttosto semplice.
bind
accetta un tipo "contenitore" e una funzione che associa il tipo che il contenitore conserva a un altro tipo. Il valore restituito è lo stesso tipo di contenitore, ma ora contiene un nuovo tipo. Un esempio sarebbe prendere uno List[Int]
e mapparlo a un List[String]
utilizzando una funzione che mappa Int
s a String
s. È bind
più o meno come map
?
L'implementazione di bind
è il punto in cui sono bloccato. Ecco il codice:
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)()
}
Questa definizione prende IO[A]
e la mappa per IO[B]
utilizzando una funzione che prende un A
e restituisce un IO[B]
. Immagino di farlo, deve usare flatMap
per "appiattire" il risultato (corretto?).
Il = IO { ... }
è lo stesso di
= new IO[A] {
def unsafePerformIO = implicitly[Monad[Function0]].bind(() => a.unsafePerformIO,
(x:A) =>() => f(x).unsafePerformIO)()
}
}
penso?
il metodo implicitly
cerca un valore implicito (valore, giusto?) Che implementa Monad[Function0]
. Da dove viene questa definizione implicita? Immagino che questo sia dalla definizione implicit val IOMonad = new Monad[IO] {...}
, ma al momento siamo all'interno di quella definizione e le cose diventano un po 'circolari e il mio cervello inizia a rimanere bloccato in un ciclo infinito :)
Inoltre, il primo argomento a bind
(() => a.unsafePerformIO
) sembra essere una funzione che non accetta parametri e restituisce a.unsafePerformIO. Come dovrei leggere questo? bind
accetta un tipo di contenitore come primo argomento, quindi forse () => a.unsafePerformIO
si risolve in un tipo di contenitore?
Scalaz fornisce effettivamente una monade IO fuori dalla scatola ora. import scalaz.effects._ – Apocalisp