2012-06-10 36 views
7

Escribiendo una biblioteca completamente asíncrona para acceder a un servicio remoto (usando Play2.0), estoy usando Promise y Validation para crear una llamada no bloqueante, que tiene un tipo que presenta error y resultado válido a la vez.Cálculo asincrónico con validación en Scala usando Scalaz

Promise viene de Play2-scala, donde Validation viene de scalaz.

Así que aquí es el tipo de ejemplos de tales funciones

  • f :: A => Promise[Validation[E, B]]
  • g :: B => Promise[Validation[E, C]]

Hasta ahora, todo va bien, ahora si quiero componerlos , Puedo usar simplemente el hecho de que Promise presento un flatMap, entonces puedo hacerlo con una comprensión forzosa

for (
    x <- f(a); 
    y <- g(b) 
) yield y 

Ok, tomé un atajo a mi problema aquí porque no reutilicé los resultados Validation dentro de la comprensión. Así que si quiero volver a utilizar x en g, aquí es cómo yo podía hacer

for (
    x <- f(a); // x is a Validation 
    y <- x.fold(
     fail => Promise.pure(x), 
     ok => g(ok) 
    ) 
) yield y 

Justo lo suficiente, pero este tipo de texto modelo irá a contaminar mi código una y otra vez. El problema aquí es que tengo un tipo de estructura Monadic de dos niveles como M[N[_]].

En esta etapa, ¿hay alguna estructura en la f ° de programación que permite trabajar con dicha estructura por saltarse fácilmente el nivel secong:

for (
    x <- f(a); //x is a B 
    y <- g(b) 
) yield y 

Ahora, a continuación es la forma en que he logrado algo similar.

creé tipo de estructura monádica que envuelve el nivel dos en uno, vamos a decir ValidationPromised la que chulo el tipo Promise con dos métodos:

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 
     ) 
    } 

Esto me permite hacer este tipo de cosas

 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 
     ) 
     ) 
    ) 

Como podemos ver /~~> es bastante similar a flatMap pero se saltea un nivel. El problema es la verbosidad (es por eso que "for-comprensión" existe en Scala y "do" en Haskell).

Otro punto, no tengo la /~> que se destaca como un map también funciona, pero en el segundo nivel (en lugar del tipo XHTML - tercer nivel)

Así que mi segunda pregunta es corolario de la antigua ... ¿Estoy buscando una solución sostenible con esta construcción?

Siento ser tan largo

+0

He estado pensando en usar ScalaZ con mis aplicaciones de Play por un tiempo, y este es un buen empujón para mí. Te contaré cómo me llevo, y con un poco de suerte podré dar una respuesta significativa aquí. – opyate

+0

¡Sí! Gracias. En realidad, el problema real no es usar ScalaZ con Play. Es una pregunta más general (sobre f ° prog), porque sin Play (tan solo ScalaZ) usé 'IO' en lugar de' Promise'. Por lo tanto, tendría el mismo patrón, es decir: 'IO [Validación [E, A]]' –

Respuesta

4

El concepto que busca aquí es monad transformers. En resumen, los transformadores de mónada compensan monads not composing permitiéndole "apilarlos".

No mencionó la versión de Scalaz que está utilizando, pero si mira en el scalaz-seven branch, encontrará ValidationT. Esto se puede utilizar para ajustar cualquier F[Validation[E, A]] en un ValidationT[F, E, A], donde en su caso F = Promise. Si cambia fg y volver ValidationT, entonces se puede salir de su código como

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

Esto le dará una ValidationT[Promise, E, B] como resultado.

+0

¡¡Mmm, miro el tipo de cosas que estoy buscando !! Ok, intentaré primero con el transformador de mónada, por dos razones, porque es la única forma de aprender, y, en segundo lugar, porque estoy en la rama 6.0.4. Y luego probablemente me mueva al 7mo, y refactorice para el ValidatorT ... En todos los casos, volveré aquí para contar más. Gracias de nuevo –

+0

Ok. Gran doc. Miré en el 'ValidationT' su método' flatMap' es de hecho lo que necesito (y supongo que es lo mismo que mi '/ ~~>'). Pero para usarlo directamente, 'Promesa' necesita una instancia de TypeClass de 'Monad', que no es necesaria. Entonces, cuando salga ScalaZ, lo implementaré para poder usar 'ValidationT' directamente. –