如何组合 Kleisli 序列
How to combine sequence of Kleisli
给定一个方法return一个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)
}
有没有更好的方法?
有更好的方法:
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 没有为 Seq
提供 Traverse
实例(参见我的回答 here 讨论为什么不这样做)。
这是首先使用 Kleisli
的一大优势——如果 F
有一个 Applicative
实例,那么 Kleisli[F, In, ?]
对任何 In
。在这种情况下,这意味着您可以使用 traverse
而不是使用 map
和 Future.sequence
.
手动排序
更新:想在这里得到超级喜欢?可能不是,但为了以防万一,您实际上可以进行最后一步抽象并将 return 类型的上下文移动到 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)
你真的应该这样做吗?同样,可能不是,但它确实有效,并且能够将方式映射到像这样的复杂计算中真是太酷了。
给定一个方法return一个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)
}
有没有更好的方法?
有更好的方法:
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 没有为 Seq
提供 Traverse
实例(参见我的回答 here 讨论为什么不这样做)。
这是首先使用 Kleisli
的一大优势——如果 F
有一个 Applicative
实例,那么 Kleisli[F, In, ?]
对任何 In
。在这种情况下,这意味着您可以使用 traverse
而不是使用 map
和 Future.sequence
.
更新:想在这里得到超级喜欢?可能不是,但为了以防万一,您实际上可以进行最后一步抽象并将 return 类型的上下文移动到 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)
你真的应该这样做吗?同样,可能不是,但它确实有效,并且能够将方式映射到像这样的复杂计算中真是太酷了。