Ho visto un video di recente su come si poteva venire con la monade IO, il discorso era in scala. In realtà mi sto chiedendo quale sia il motivo per cui le funzioni restituiscono IO [A] al di fuori di esse. Le espressioni lambda avvolte nell'oggetto IO sono ciò che le mutazioni sono e, a un certo punto, più in alto nel cambiamento che devono essere osservate, voglio dire eseguito, in modo che qualcosa accada. Non stai solo spingendo il problema più in alto nell'albero da qualche altra parte?Scala IO monade: qual è il punto?
L'unico vantaggio che posso vedere è che consente una valutazione lenta, nel senso che se non si chiama l'operazione unsafePerformIO non si verificano effetti collaterali. Immagino anche che altre parti del programma possano usare/condividere codice e deciede quando vuole che si verifichino gli effetti collaterali.
Mi chiedevo se questo è tutto? C'è qualche vantaggio nella testabilità? Sto assumendo non come si dovrebbe osservare gli effetti che nega questo tipo. Se hai usato tratti/interfacce puoi controllare le dipendenze ma non quando gli effetti si verificano su queste dipendenze.
Ho messo insieme il seguente esempio nel codice.
case class IO[+A](val ra:() => A){
def unsafePerformIO() : A = ra();
def map[B](f: A => B) : IO[B] = IO[B](() => f(unsafePerformIO()))
def flatMap[B](f: A => IO[B]) : IO[B] = {
IO(() => f(ra()).unsafePerformIO())
}
}
case class Person(age: Int, name: String)
object Runner {
def getOlderPerson(p1: Person,p2:Person) : Person =
if(p1.age > p2.age)
p1
else
p2
def printOlder(p1: Person, p2: Person): IO[Unit] = {
IO(() => println(getOlderPerson(p1,p2))).map(x => println("Next"))
}
def printPerson(p:Person) = IO(() => {
println(p)
p
})
def main(args: Array[String]): Unit = {
val result = printPerson(Person(31,"Blair")).flatMap(a => printPerson(Person(23,"Tom"))
.flatMap(b => printOlder(a,b)))
result.unsafePerformIO()
}
}
Si può vedere come gli effetti vengono differiti fino al main che immagino sia freddo. L'ho inventato dopo averlo provato dal video.
La mia implementazione è corretta e la mia comprensione è corretta.
Mi chiedo anche se per ottenere milage dovrebbe essere combinato con ValidationMonad, come in ValidationMonad [IO [Person]] in modo da poter cortocircuitare quando si verificano eccezioni? Pensieri per favore.
Blair
Grazie. Grande risposta darò un'occhiata a quelle idee stasera. –
Hai uno snippet di codice con sottoclassi e classi di tipi in mente? –
Vedere le diapositive di Runar a cui Drexin si collega, in particolare le cose 'ConsoleIO'. Dimostra la separazione della dichiarazione di quali azioni IO esistono ('caso caso GetLine ...', 'case class PutLine ... ') dalla definizione di cosa potrebbe accadere se esegui quelle azioni (' oggetto implicito ConsoleEffect .. .'). Ma nota che ci sono anche altre cose lì dentro; non è un codice minimale che dimostra solo _ quello che ho detto. –