monad变换器是否适用于从服务中获取JSON?

And*_*rea 18 monads scala monad-transformers playframework-2.0

我玩了!2用于Scala应用程序,需要从外部服务检索JSON格式的某些数据.

表演!框架允许通过将响应包装在Promise中来异步发出HTTP请求.Promise是一个monad,它包含了将来可用的值.

这很好,但在我的情况下,我从Web服务得到的是一个JSON字符串.我必须解析它,解析可能会失败.所以我必须包装我得到的任何东西Option.结果是我的许多方法都在返回Promise[Option[Whatever]].也就是说,类型的值Whatever可能稍后可用.

现在每当我必须操作这样的值时,我需要map两次.我想用以下方式处理这个问题:

  • 创建一个新类型,比如说Hope[A],包装一个Promise[Option[A]]
  • 定义相关的方法,如map(或者我应该使用foreach和继承某些集合特征?)和flatten
  • Promise[Option[A]]和之间提供隐式转换器Hope[A].

它很容易定义map- 两个仿函数的组合也是一个仿函数 - flatten在这种情况下可以明确地完成,或者在编写monad时Option.

但是我有限的理解是我不需要重新发明这些东西:monad变换器确实存在于这种情况下.或者,好吧,所以我认为 - 我从未使用过monad变换器 - 这就是问题的关键点:

在这种情况下可以使用monad变压器吗?我将如何实际使用它们?

Ben*_*mes 14

使用Scalaz库的OptionT转换器,您应该能够将类型的Promise[Option[A]]值转换为类型的值OptionT[Promise, A].

使用Scalaz 7:

import scalaz.OptionT._
val x: OptionT[Promise, Int] = optionT(Promise.pure(Some(123)))
Run Code Online (Sandbox Code Playgroud)

要使用此值,例如调用map或调用flatMap它,您需要为Promise(Functorfor map,Monadfor flatMap)提供适当的类型类.

由于Promise是monadic,应该可以提供一个实例Monad[Promise].(你会得到FunctorApplicative自由,因为类型类形成的继承层次.)例如(注:我没有测试这个!):

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
}
Run Code Online (Sandbox Code Playgroud)

作为一个简单的例子,你现在可以使用mapOptionT[Promise, A],以类型的函数适用A => B于内部的价值:

def foo[A, B](x: OptionT[Promise, A], f: A => B): OptionT[Promise, B] = x map f
Run Code Online (Sandbox Code Playgroud)

要从a检索基础Promise[Option[A]]OptionT[Promise, A],请调用该run方法.

def bar[A, B](x: Promise[Option[A]], f: A => B): Promise[Option[B]] =
  optionT(x).map(f).run
Run Code Online (Sandbox Code Playgroud)

当您可以组合兼容类型的多个操作时,您将从使用monad变换器中获得更多好处,OptionT[Promise, _]在操作之间保留类型并在最后检索基础值.

要在for-comprehension中组合操作,您将需要类型的函数A => OptionT[Promise, B].