2012-06-10 10 views
7

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

+0

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

+0

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]] ' –

risposta

4

Il concetto che si sta cercando qui è monad transformers. In breve, i trasformatori monad compensano monads not composing consentendo di "impilarli".

Non hai menzionato la versione di Scalaz che stai utilizzando, ma se guardi nello scalaz-seven branch, troverai ValidationT. Questo può essere utilizzato per avvolgere qualsiasi F[Validation[E, A]] in un ValidationT[F, E, A], dove nel tuo caso F = Promise. Se si cambia f e g tornare ValidationT, allora si può lasciare il vostro codice come

for { 
    x ← f(a) 
    y ← g(b) 
} yield y 

Questo vi darà un ValidationT[Promise, E, B] di conseguenza.

+0

MMmh guarda il genere di cosa che sto cercando !! Ok, quindi proverò prima con il trasformatore monade, per due ragioni perché è l'unico modo per imparare, e in secondo luogo perché sono sul ramo 6.0.4. E poi probabilmente mi sposterò al settimo e rifatterò per il ValidatorT ... In tutti i casi, tornerò qui per saperne di più. Grazie ancora –

+0

Ok. Grande doc Ho esaminato il 'ValidationT' il suo metodo' flatMap' è davvero quello di cui ho bisogno (e come indovinare è lo stesso di quello mio// ~~>). Ma per poterlo usare direttamente, 'Promise' ha bisogno di un'istanza TypeClass di' Monad', che non fa. Così quando uscirà ScalaZ, lo implementerò per poter usare 'ValidationT' direttamente. –