如何组合 Kleisli 的序列 [英] How to combine sequence of Kleisli

查看:38
本文介绍了如何组合 Kleisli 的序列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给定一个返回 Kleisli 给定参数的方法

Given a method that return a Kleisli given a parameter

def k[A, B, C](a: A) : Kleisli[Future, B, Option[C]] = ???

我想写一个组合子来处理这个参数的序列

I want to write a combinator that deals with a sequence of this parameter

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 没有为 SeqTraverse 实例代码>(请参阅我的回答此处,以了解为什么不这样做的一些讨论).

Note that I'm using List instead of Seq since Scalaz doesn't provide a Traverse instance for Seq (see my answer here for some discussion of why it doesn't).

这是首先使用 Kleisli 的一大优势——如果 F 有一个 Applicative 实例,那么Kleisli[F, In, ?] 用于任何 In.在这种情况下,这意味着您可以使用 traverse 而不是使用 mapFuture.sequence 手动排序.

This is one of the big advantages of using Kleisli in the first place—if F has an Applicative instance, then so does Kleisli[F, In, ?] for any In. In this case that means that you can use traverse instead of manually sequencing with map and Future.sequence.

更新:想在这里变得超级花哨吗?可能不是,但为了以防万一,您实际上可以在最后一步进行抽象并将返回类型的上下文移动到 Kleisli 的上下文中:

Update: want to get super-fancy here? Probably not, but just in case, you could actually take the abstraction one last step and move the context of the return type into the context of the 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 应用到输入值后,我们将在未来在列表中获得该结果的事实.

This allows us to map, etc. directly over the result type while ignoring the fact that we're getting that result in a list in a future after applying the Kleisli to an input value.

具体:

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)

你真的应该这样做吗?同样,可能不会,但它有效,并且能够将方式映射到这样的复杂计算中是很酷的.

Should you actually do this? Again, probably not, but it works, and it is kind of cool to be able to map way into a complex computation like this.

这篇关于如何组合 Kleisli 的序列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆