Durante la scrittura di una libreria completamente asincrona per accedere a un servizio remoto (utilizzando Play2.0), sto usando Promise
e Validation
per creare una chiamata non bloccante, che presenta un tipo che presenta errori e risultato valido in una volta.Calcolo asincrono con Validazione in Scala utilizzando Scalaz
Promise
proviene da Play2-scala, dove Validation
deriva da scalaz.
Così qui è il tipo di esempi di tali funzioni
- f ::
A => Promise[Validation[E, B]]
- g ::
B => Promise[Validation[E, C]]
Fin qui, tutto bene, ora se voglio comporli , Posso semplicemente usare il fatto che Promise
presenti uno flatMap
, quindi posso farlo con una comprensione preliminare
for (
x <- f(a);
y <- g(b)
) yield y
Ok, ho preso una scorciatoia per il mio problema qui perché non ho riutilizzato i risultati Validation
all'interno della comprensione. Quindi, se voglio riutilizzare x
in g
, ecco come avrei potuto fare
for (
x <- f(a); // x is a Validation
y <- x.fold(
fail => Promise.pure(x),
ok => g(ok)
)
) yield y
Va bene, ma questo tipo di boilerplate andrà ad inquinare il mio codice più e più volte. Il problema qui è che ho una sorta di struttura monadica a due livelli come M[N[_]]
.
In questa fase, c'è qualche struttura in f ° programmazione che permette di lavorare con tale struttura saltando facilmente il livello secong:
for (
x <- f(a); //x is a B
y <- g(b)
) yield y
Ora, sotto è come ho raggiunto qualcosa di simile.
ho creato tipo di struttura Monadica che avvolge i due livelli in uno, diciamo ValidationPromised
che pimped tipo Promise
con due metodi:
def /~> [EE >: E, B](f: Validation[E, A] => ValidationPromised[EE, B]): ValidationPromised[EE, B] =
promised flatMap { valid =>
f(valid).promised
}
def /~~>[EE >: E, B](f: A => ValidationPromised[EE, B]): ValidationPromised[EE, B] =
promised flatMap { valid =>
valid.fold (
bad => Promise.pure(KO(bad)),
good => f(good).promised
)
}
Questo mi permette di fare queste cose
endPoint.service /~~> //get the service
(svc => //the service
svc.start /~~> (st => //get the starting elt
svc.create(None) /~~> //svc creates a new elt
(newE => //the created one
newEntry.link(st, newE) /~~> //link start and the new
(lnk => Promise.pure(OK((st, lnk, newE)))) //returns a triple => hackish
)
)
)
Come possiamo vedere, /~~>
è simile a flatMap
ma salta un livello. Il problema è la verbosità (ecco perché "for-comprehension" esiste in Scala e "do" in Haskell).
Un altro punto, ho la /~>
che si erge come un map
anche, ma lavora al secondo livello (invece del tipo valido - terzo livello)
Quindi la mia seconda domanda è corollario l'ex ... Sto approciando una soluzione sostenibile con questa costruzione?
Mi dispiace essere così a lungo
Ho intenzione di usare ScalaZ con le mie app Play per un po 'di tempo, e questo è un buon spunto per me. Ti farò sapere come andrò avanti e, si spera, riuscirò a dare una risposta significativa qui. – opyate
Sì! Grazie. In realtà il vero problema non sta usando ScalaZ con Play. È una domanda più generale (riguardo al f ° prog), perché senza Play (quindi solo ScalaZ) avevo usato 'IO' invece di' Promise'. Quindi, avrei lo stesso schema, cioè: IO [Validazione [E, A]] ' –