2017-10-14 48 views
4

我想知道是否有方法将List[Kleisli[Option, Int, Int]]转换为Kleisli[Option, Int, List[Int]]列表中的Kleisli到Kleisli列表

特别是我有这样形成kleisli名单:

我要做的就是以下

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

这是非常凌乱,没有表现,需要大量的人工劳动。

有没有更好的方法?

+1

除了下面的很好的答案之外,我想强调一个事实,即“序列映射”(即序列映射)做'map'然后做'sequence')在一般情况下等价于'遍历'。这可能会帮助你重构你的代码库的其他几个部分;) –

回答

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文件,你可以只使用traversetraverseU的地方。此外,从版本1.0.0开始,traverseUsequenceU将不再存在。

请注意,如果您使用的是Scala < 2.11.9但> = 2.10.6,您仍然可以通过将this plugin添加到您的版本来启用部分统一。

+2

我会添加(正如Luka在他的回答中所说的),在1.0.0版本的猫中,你需要'-Y-partial-unification'标志来'遍历'编译;) –

+0

是的,我没有写它,因为它明确表示在猫的github页面,但你是对的,添加它不会有害。 ;) – lambdista

+0

为什么我们需要旗帜寿?为什么这不是默认? – gurghet

4

,你可以做最简单的就是有partial-unification启用并使用traverse

import cats.implicits._ 

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

这是一样的对你kList运行sequence,为traverse相当于map然后sequence

启用partial-unification的最简单方法是添加sbt-partial-unificationplugin。从cats

scalacOptions += "-Ypartial-unification" 

我们强烈建议您有此标志在任何时候使用时:

如果你在斯卡拉2.11.9或更新的版本,你也可以简单地添加了编译器标志猫,因为它使一切变得容易很多。

+0

你确定启用部分统一功能也适用于0.9.0版本的猫吗?我认为他们删除了未启用的方法,例如sequenceU,在1.0.0-MF中遍历U.在这种情况下,您的代码不适用于0.9.0版本。 – lambdista

+0

是的,它将适用于所有版本的猫:) –

+2

我看到,traverseU和sequenceU在1.0.0-MF中被删除,但另一方面,即使在以前版本的猫中,部分统一也会导致遍历工作。 – lambdista

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