给定一个方法,在给定参数
的情况下返回Kleisli
def k[A, B, C](a: A) : Kleisli[Future, B, Option[C]] = ???
我想编写一个处理此参数序列的组合器
def ks[A, B, C](as: Seq[A]) : Kleisli[Future, B, Seq[C]] = Kleisli[Future, B, Seq[C]] {
ctx => Future.sequence(as.map(a => k(a).run(ctx))).map(_.flatten)
}
有更好的方法吗?
答案 0 :(得分:3)
有一种更好的方法:
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scalaz._, Scalaz._
def k[A, B, C](a: A): Kleisli[Future, B, Option[C]] = ???
def ks[A, B, C](as: List[A]): Kleisli[Future, B, List[C]] =
as.traverseU(k[A, B, C]).map(_.flatten)
请注意,我使用的是List
而不是Seq
,因为Scalaz没有为Traverse
提供Seq
个实例(请参阅我的回答here讨论为什么不这样做。
这是首先使用Kleisli
的一大优势 - 如果F
有Applicative
个实例,那么任何Kleisli[F, In, ?]
In
也是如此}}。在这种情况下,这意味着您可以使用traverse
而不是使用map
和Future.sequence
手动排序。
更新:想在这里超级幻想?可能不是,但为了以防万一,您实际上可以采取抽象的最后一步,并将返回类型的上下文移动到Kleisli
的上下文中:
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scalaz._, Scalaz._
type FutureOption[x] = OptionT[Future, x]
type FutureList[x] = ListT[Future, x]
def k[A, B, C](a: A): Kleisli[FutureOption, B, C] = ???
def ks[A, B, C](as: List[A]): Kleisli[FutureList, B, C] =
Kleisli.kleisliMonadTrans[B].liftMU(
ListT.fromList(as.point[Future])
).flatMap(k[A, B, C](_).mapK[FutureList, C](_.toListT))
这允许我们直接在结果类型上进行映射等,同时忽略在将Kleisli
应用于输入值后我们将来在列表中获得该结果的事实。
具体地:
import scala.util.Try
def k(s: String): Kleisli[FutureOption, Int, Int] = Kleisli[FutureOption, Int, Int] { in =>
OptionT(Future(Try(s.toInt + in).toOption))
}
def ks(as: List[String]): Kleisli[FutureList, Int, Int] =
Kleisli.kleisliMonadTrans[Int].liftMU(
ListT.fromList(as.point[Future])
).flatMap(k(_).mapK[FutureList, Int](_.toListT))
然后:
import scala.concurrent.Await
import scala.concurrent.duration._
scala> import scala.concurrent.Await
import scala.concurrent.Await
scala> import scala.concurrent.duration._
import scala.concurrent.duration._
scala> Await.result(ks(List("1", "2", "a", "3")).run(0).run, 1.second)
res0: List[Int] = List(1, 2, 3)
和妙语:
scala> val mapped = ks(List("1", "2", "a", "3")).map(_ + 1)
mapped: scalaz.Kleisli[FutureList,Int,Int] = Kleisli(<function1>)
scala> Await.result(mapped.run(0).run, 1.second)
res1: List[Int] = List(2, 3, 4)
你真的应该这样做吗?同样,可能不是,但是它有效,并且能够将方式映射到这样的复杂计算中是一种很酷的。