5

我想知道是否有办法List[Kleisli[Option, Int, Int]]转向Kleisli[Option, Int, List[Int]].

特别是我有这样的 kleisli 列表:

def k(a: String) = Kleisli[Option, Int, Int](m => Some(a.length * m))
val kList = List("hi", "hello").map(k)

我做的是以下

Kleisli[Option, Int, List[Int]](m => kList.map(_.run(m)).sequence)

这非常混乱,没有表现力,需要大量的手工工作。

有没有更好的办法?

4

3 回答 3

6

是的,您可以使用traversewhich 来做到这一点。如果您使用的是cats<= 0.9.0,则可以使用以下代码:

import cats.data._
import cats.instances.list._
import cats.instances.option._
import cats.syntax.traverse._

// ...
def k(a: String) = Kleisli[Option, Int, Int](m => Some(a.length * m))
val result: Kleisli[Option, Int, List[Int] = List("hi", "hello").traverseU(k)

如果您使用的是 Scala 2.11.9+,通过添加scalacOptions += "-Ypartial-unification"到您的文件中,build.sbt您可以使用traverse. traverseU另外,从版本 1.0.0 开始,traverseUsequenceU不再存在。

请注意,如果您使用的是 Scala < 2.11.9 但 >= 2.10.6,您仍然可以通过将此插件添加到您的构建中来启用部分统一。

于 2017-10-14T00:58:30.807 回答
4

您可以做的最简单的事情是partial-unification启用并使用traverse

import cats.implicits._

List("hi", "hello").traverse(k)

sequence这与在您的 上运行相同kList,因为traverse等效于map然后sequence

最简单的启用方法partial-unification是添加sbt-partial-unification 插件

如果您使用的是 Scala 2.11.9 或更高版本,您还可以简单地添加编译器标志:

scalacOptions += "-Ypartial-unification"

团队中的我们cats强烈建议您在使用猫时始终打开此标志,因为它使一切变得容易得多。

于 2017-10-14T01:08:40.377 回答
2

使用TraverseOps.sequence我们可以转换List[A[B]]A[List[B]], 其中

A = ({type λ[α] = Kleisli[Option, Int, α]})#λ
B = Int

所以答案是:

def transform(x: List[Kleisli[Option, Int, Int]]) =
  x.sequence[({type λ[α] = Kleisli[Option, Int, α]})#λ, Int]

以下代码是完整的解决方案:

import scalaz._
import Scalaz._
import scalaz.Kleisli._

def transform(x: List[Kleisli[Option, Int, Int]]) = x.sequence[({type λ[α] = Kleisli[Option, Int, α]})#λ, Int]

def k(a: String) = Kleisli[Option, Int, Int](m => Some(a.length * m))
val kList = List("hi", "hello").map(k)
val res = transform(kList)
res.run(10)

https://scastie.scala-lang.org/2uZvWWb1ScOHNA55QOcWQA

于 2017-10-14T03:19:26.880 回答