Kleisli列表到列表的Kleisli
List of Kleisli to Kleisli of list
我想知道有没有办法把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)
非常凌乱,没有表现力,需要大量手工工作。
有没有更好的方法?
是的,您可以使用 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版本开始,traverseU
和sequenceU
将不再存在。
请注意,如果您使用的是 Scala < 2.11.9 但 >= 2.10.6,您仍然可以通过将 this plugin 添加到您的构建中来启用部分统一。
最简单的方法是启用 partial-unification
并使用 traverse
:
import cats.implicits._
List("hi", "hello").traverse(k)
这与 kList
上的 运行 sequence
相同,因为 traverse
相当于 map
然后 sequence
。
启用 partial-unification
的最简单方法是添加 sbt-partial-unification
plugin.
如果您使用的是 Scala 2.11.9 或更新版本,您还可以简单地添加编译器标志:
scalacOptions += "-Ypartial-unification"
我们 cats
团队强烈建议您在使用猫时始终打开此标志,因为它使一切变得容易得多。
使用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)
我想知道有没有办法把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)
非常凌乱,没有表现力,需要大量手工工作。
有没有更好的方法?
是的,您可以使用 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版本开始,traverseU
和sequenceU
将不再存在。
请注意,如果您使用的是 Scala < 2.11.9 但 >= 2.10.6,您仍然可以通过将 this plugin 添加到您的构建中来启用部分统一。
最简单的方法是启用 partial-unification
并使用 traverse
:
import cats.implicits._
List("hi", "hello").traverse(k)
这与 kList
上的 运行 sequence
相同,因为 traverse
相当于 map
然后 sequence
。
启用 partial-unification
的最简单方法是添加 sbt-partial-unification
plugin.
如果您使用的是 Scala 2.11.9 或更新版本,您还可以简单地添加编译器标志:
scalacOptions += "-Ypartial-unification"
我们 cats
团队强烈建议您在使用猫时始终打开此标志,因为它使一切变得容易得多。
使用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)