列表Kleisli to Kleisli列表

时间:2017-10-14 00:00:19

标签: scala scala-cats kleisli

我想知道是否有办法将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)

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

有更好的方法吗?

3 个答案:

答案 0 :(得分:6)

是的,您可以使用traverse来做到这一点。如果您使用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您仍然可以通过向构建中添加this plugin来启用部分统一。

答案 1 :(得分:4)

您最简单的方法是启用partial-unification并使用traverse

import cats.implicits._

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

这与在sequence上投放kList相同,因为traverse相当于map,然后是sequence

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

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

scalacOptions += "-Ypartial-unification"

我们来自cats团队强烈建议您在使用猫时始终使用此标记,因为它使一切变得更加容易。

答案 2 :(得分: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