scala - ¿Se aplican los transformadores de mónada para obtener JSON de los servicios?
playframework-2.0 monads (2)
- eliminado
editar:
Bien, simplemente puedes usar el scalaz.OptionT
aquí:
val x = optionT(Promise { /* api call */ some("""{ "foo": "bar" }""") })
val mapped = x.map(Json.parse).run // run return the resulting Promise[Option[T]]
¡Tengo un juego! 2 para la aplicación Scala que necesita recuperar algunos datos en formato JSON de un servicio externo.
¡El juego! framework permite realizar solicitudes HTTP de forma asíncrona envolviendo la respuesta en una Promise . Promise
es una mónada que envuelve un valor que estará disponible en el futuro.
Esto está bien, pero en mi caso, lo que recibo del servicio web es una cadena JSON. Tengo que analizarlo y el análisis podría fallar. Así que tengo que envolver lo que sea que consiga en una Option
. El resultado es que muchos de mis métodos devuelven Promise[Option[Whatever]]
. Es decir, un valor de tipo Whatever
que sea, tal vez, esté disponible más adelante.
Ahora, cada vez que tengo que operar sobre un valor así, necesito map
dos veces. Estaba pensando en manejar esto de la siguiente manera:
- creando un nuevo tipo, digamos
Hope[A]
, que envuelve unaPromise[Option[A]]
- definiendo los métodos relevantes como el
map
(o tal vez debería usarforeach
y heredar de algún rasgo de colección) yflatten
- proporcione un convertidor implícito entre
Promise[Option[A]]
yHope[A]
.
Es fácil definir el map
(la composición de dos funtores es nuevamente un funtor) y en este caso, el flatten
se puede hacer explícitamente, o siempre que se componga una mónada con Option
.
Pero mi entendimiento limitado es que no necesito reinventar esto: el transformador de mónada existe exactamente para este caso. O bien, creo que, nunca he usado un tranformer de mónada, y este es el punto de la pregunta:
¿Se pueden usar transformadores de mónadas en esta situación? ¿Cómo podría realmente usarlos?
Al usar el transformador OptionT
la biblioteca OptionT
, debería poder convertir valores de tipo Promise[Option[A]]
en valores de tipo OptionT[Promise, A]
.
Utilizando Scalaz 7:
import scalaz.OptionT._
val x: OptionT[Promise, Int] = optionT(Promise.pure(Some(123)))
Para usar este valor, por ejemplo, para llamar a map
o flatMap
, deberá proporcionar una clase de tipos apropiada para Promise
( Functor
for map
, flatMap
para flatMap
).
Como Promise
es monádica, debería ser posible proporcionar una instancia de Monad[Promise]
. (Obtendrá Functor
y Applicative
gratis, porque las clases de tipos forman una jerarquía de herencia.) Por ejemplo (nota: ¡No he probado esto!):
implicit val promiseMonad = new Monad[Promise] {
def point[A](a: => A): Promise[A] = Promise.pure(a)
def bind[A, B](fa: Promise[A])(f: A => Promise[B]): Promise[B] = fa flatMap f
}
Como ejemplo simple, ahora puede usar el map
en la OptionT[Promise, A]
, para aplicar una función de tipo A => B
al valor que se encuentra dentro:
def foo[A, B](x: OptionT[Promise, A], f: A => B): OptionT[Promise, B] = x map f
Para recuperar el valor subyacente de la Promise[Option[A]]
de una OptionT[Promise, A]
, llame al método de run
.
def bar[A, B](x: Promise[Option[A]], f: A => B): Promise[Option[B]] =
optionT(x).map(f).run
Obtendrá más beneficios con el uso de transformadores de mónada cuando pueda componer varias operaciones de tipos compatibles, conservando el tipo OptionT[Promise, _]
entre las operaciones y recuperando el valor subyacente al final.
Para componer operaciones en una comprensión, necesitará funciones de tipo A => OptionT[Promise, B]
.