2017-10-14 57 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