处理嵌套Monad的更好方法?

时间:2015-08-24 00:44:16

标签: scala

给出一个名单:

scala> import scala.concurrent.Future
import scala.concurrent.Future

scala> import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global

scala> val names: Future[List[String]] = 
            Future.successful( List("Joe Foo", "Jane Bar") )
names: scala.concurrent.Future[List[String]] = 
           scala.concurrent.impl.Promise$KeptPromise@3dddbe65

并且,对于给定的name,该方法为该名称的业余爱好返回Future[String]

scala> def getHobby(name: String): Future[String] = Future.successful( "poker" )
getHobby: (name: String)scala.concurrent.Future[String]

使用names,即Future[List[String]],我可以获得这些名称'爱好来自:

scala> names.map(_.map(getHobby))
res3: scala.concurrent.Future[List[scala.concurrent.Future[String]]] =
         scala.concurrent.impl.Promise$DefaultPromise@42c28305

但是,我有一个难以阅读的嵌套monad,Future[List[Future[String]]

我可以清理它:

scala> res3.map(Future.sequence(_))
res5: scala.concurrent.Future[scala.concurrent.Future[List[String]]] = 
      scala.concurrent.impl.Promise$DefaultPromise@4eaa375c

scala> res5.flatMap(identity)
res6: scala.concurrent.Future[List[String]] = 
   scala.concurrent.impl.Promise$DefaultPromise@101bdd1c

然后得到它的价值。

scala> res6.value
res7: Option[scala.util.Try[List[String]]] = Some(Success(List(poker, poker)))

但是,是否有更清洁,更惯用的方式来完成上述工作?

2 个答案:

答案 0 :(得分:4)

一种方法是在检索数据时将其展平。

scala> names.flatMap(x => Future.sequence(x.map(getHobby)))
res54: scala.concurrent.Future[List[String]] = scala.concurrent.impl.Promise$DefaultPromise@45cd45aa

答案 1 :(得分:2)

仅为了说明(嵌套monad),你还有{34} Scalaz Monad Transformers",由Rama Nallamilli描述。

  

img's为例,以下所有这些情况都是嵌套在monad中的monad。

Future

对于Future[Option[T]] Future[List[T]] Future[scalaz.\/[A, B]] 这样的嵌套monad,Rama建议:

  

当选择使用哪个monad变换器时,你总是选择最内层的类型,在这种情况下,List [Int]是我们最内部的类型,所以我们将使用(Scalaz)Future[List[Int]] monad变换器。

     

ListT应用功能如下:

ListT
  

因此,我们可以使用此功能将def apply[A](a: M[List[A]]) = new ListT[M, A](a) 转换为Future[List[Int]] monad类型,在本例中为ListT。   我们现在可以用新的monad类型来编写我们的补充,它已经抽象了ListT[Future, Int]的映射:

Future

总结:

  

monad变换器为你提供了一个强大的抽象来处理monadic类型的底层数据,当它本身包含在monad中时。
  它通过抽取钻取到嵌套数据类型的布线来降低代码复杂性并增强可读性。

     

ScalaZ为许多类型提供了monad变换器的实现,包括EitherT,ListTOptionTReaderT等等。