Cálculo asíncrono con validación en Scala usando Scalaz
Al estar escribiendo una biblioteca completamente asíncrona para acceder a un servicio remoto (usando Play2.0), estoy usandoPromise
yValidation
para crear una llamada no bloqueante, que tiene un tipo que presenta un error y un resultado válido a la vez.
Promise
viene de play2-scala, dondeValidation
viene de scalaz.
Así que aquí está el tipo de ejemplos de tales funciones.
f ::A => Promise[Validation[E, B]]
g ::B => Promise[Validation[E, C]]
Hasta ahora, todo bien, ahora, si quiero componerlos, puedo usar simplemente el hecho de quePromise
presentar unflatMap
, asi lo puedo hacer con una comprension
for (
x <- f(a);
y <- g(b)
) yield y
Ok, tomé un atajo a mi problema aquí porque no reutilizé elValidation
Resultados dentro de la para-comprensión. Así que si quiero reutilizar.x
eng
, aquí es como podrí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 repetición irá a contaminar mi código una y otra vez. El problema aquí es que tengo una especie de estructura monádica de dos niveles comoM[N[_]]
.
En esta etapa, ¿hay alguna estructura en la programación en grados que permita trabajar con dicha estructura saltando fácilmente el segundo nivel:
for (
x <- f(a); //x is a B
y <- g(b)
) yield y
Ahora, a continuación es cómo logré algo similar.
Creé una especie de estructura monádica que envuelve los dos niveles en uno, digamosValidationPromised
el cual engullía elPromise
tipo 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 esas 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 aflatMap
pero se salta un nivel. El problema es la verbosidad (es por eso que "para-comprensión" existe en Scala y "hacer" en Haskell).
Otro punto, tengo el/~>
eso es como unmap
también, pero funciona en el segundo nivel (en lugar del tipo Válido -tercero nivel)
Entonces, mi segunda pregunta es corolario de la anterior ... ¿Estoy buscando una solución sostenible con esta construcción?
Lamento ser tan largo