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