Scala 猫,遍历 Seq
Scala cats, traverse Seq
我知道我可以遍历List
s
import cats.instances.list._
import cats.syntax.traverse._
def doMagic(item: A): M[B] = ???
val list: List[A] = ???
val result: M[List[B]] = list.traverse(doMagic)
而且我可以将 Seq
来回转换为 List
val seq: Seq[A] = ???
val result: M[Seq[B]] = seq.toList.traverse(doMagic).map(_.toSeq)
但是我也可以在没有样板的情况下遍历 Seq
吗?
val seq: Seq[A] = ???
val result: M[Seq[B]] = seq.traverse(doMagic)
或者获取 Traverse[Seq] 实例的简单方法是什么?
Cats 不为 Seq
提供类型类实例,因此除了自己实现它之外,您还需要进行转换。
至于为什么,在(有点旧的)Cats issue 中正在进行讨论。总而言之,您对 Seq
基本特征的了解不足以确保某些类型类实例法则成立。
编辑:没关系,它现在存在,请参阅链接线程
如果您完全确定从所有 Seq
到 List
的转换在您的代码中总是会成功,您可以简单地从 List
转移 Traverse
结构在(伪)同构上 Seq
:
def traverseFromIso[F[_], Z[_]]
(forward: F ~> Z, inverse: Z ~> F)
(implicit zt: Traverse[Z])
: Traverse[F] = new Traverse[F] {
def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) ⇒ B): B = zt.foldLeft(forward(fa), b)(f)
def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
zt.foldRight(forward(fa), lb)(f)
def traverse[G[_], A, B]
(fa: F[A])
(f: (A) ⇒ G[B])
(implicit appG: Applicative[G])
: G[F[B]] = {
(zt.traverse(forward(fa))(f)(appG)).map(zb => inverse(zb))
}
}
这并不是真正的同构,因为从 Seq
到 List
的转换可能会严重失败(例如,如果序列是无限的)。它所做的只是将 Seq
来回转换为 List
,并将所有方法调用转发给 Traverse[List]
.
的方法调用
现在您可以使用此方法构建 Traverse[Seq]
的实例:
implicit val seqTraverse: Traverse[Seq] = traverseFromIso(
new FunctionK[Seq, List] { def apply[X](sx: Seq[X]): List[X] = sx.toList },
new FunctionK[List, Seq] { def apply[X](lx: List[X]): Seq[X] = lx }
)
完整代码片段(使用 scala 2.12.4 和 cats 1.0.1 编译):
import cats._
import cats.implicits._
import cats.arrow.FunctionK
import scala.language.higherKinds
object TraverseFromIso {
// This method can build you a `Traversable[Seq]` from
// an `Traversable[List]` and a pair of polymorphic conversion
// functions:
def traverseFromIso[F[_], Z[_]]
(forward: F ~> Z, inverse: Z ~> F)
(implicit zt: Traverse[Z])
: Traverse[F] = new Traverse[F] {
def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) ⇒ B): B = zt.foldLeft(forward(fa), b)(f)
def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
zt.foldRight(forward(fa), lb)(f)
def traverse[G[_], A, B]
(fa: F[A])
(f: (A) ⇒ G[B])
(implicit appG: Applicative[G])
: G[F[B]] = {
(zt.traverse(forward(fa))(f)(appG)).map(zb => inverse(zb))
}
}
// A little demo
def main(args: Array[String]): Unit = {
// To instantiate a `Traverse[Seq]`, we have to provide
// two natural transformations (from List to Seq and back):
implicit val seqTraverse: Traverse[Seq] = traverseFromIso(
new FunctionK[Seq, List] { def apply[X](sx: Seq[X]): List[X] = sx.toList },
new FunctionK[List, Seq] { def apply[X](lx: List[X]): Seq[X] = lx }
)
// do stuff with `Traversable[Seq]` here
}
}
从 cats 2.3 开始,现在内置了对 immutable.Seq
的支持。请参阅“Seq 的隐式实例在哪里?”在添加功能的 FAQ or this PR 上。
我知道我可以遍历List
s
import cats.instances.list._
import cats.syntax.traverse._
def doMagic(item: A): M[B] = ???
val list: List[A] = ???
val result: M[List[B]] = list.traverse(doMagic)
而且我可以将 Seq
来回转换为 List
val seq: Seq[A] = ???
val result: M[Seq[B]] = seq.toList.traverse(doMagic).map(_.toSeq)
但是我也可以在没有样板的情况下遍历 Seq
吗?
val seq: Seq[A] = ???
val result: M[Seq[B]] = seq.traverse(doMagic)
或者获取 Traverse[Seq] 实例的简单方法是什么?
Cats 不为 Seq
提供类型类实例,因此除了自己实现它之外,您还需要进行转换。
至于为什么,在(有点旧的)Cats issue 中正在进行讨论。总而言之,您对 Seq
基本特征的了解不足以确保某些类型类实例法则成立。
编辑:没关系,它现在存在,请参阅链接线程
如果您完全确定从所有 Seq
到 List
的转换在您的代码中总是会成功,您可以简单地从 List
转移 Traverse
结构在(伪)同构上 Seq
:
def traverseFromIso[F[_], Z[_]]
(forward: F ~> Z, inverse: Z ~> F)
(implicit zt: Traverse[Z])
: Traverse[F] = new Traverse[F] {
def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) ⇒ B): B = zt.foldLeft(forward(fa), b)(f)
def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
zt.foldRight(forward(fa), lb)(f)
def traverse[G[_], A, B]
(fa: F[A])
(f: (A) ⇒ G[B])
(implicit appG: Applicative[G])
: G[F[B]] = {
(zt.traverse(forward(fa))(f)(appG)).map(zb => inverse(zb))
}
}
这并不是真正的同构,因为从 Seq
到 List
的转换可能会严重失败(例如,如果序列是无限的)。它所做的只是将 Seq
来回转换为 List
,并将所有方法调用转发给 Traverse[List]
.
现在您可以使用此方法构建 Traverse[Seq]
的实例:
implicit val seqTraverse: Traverse[Seq] = traverseFromIso(
new FunctionK[Seq, List] { def apply[X](sx: Seq[X]): List[X] = sx.toList },
new FunctionK[List, Seq] { def apply[X](lx: List[X]): Seq[X] = lx }
)
完整代码片段(使用 scala 2.12.4 和 cats 1.0.1 编译):
import cats._
import cats.implicits._
import cats.arrow.FunctionK
import scala.language.higherKinds
object TraverseFromIso {
// This method can build you a `Traversable[Seq]` from
// an `Traversable[List]` and a pair of polymorphic conversion
// functions:
def traverseFromIso[F[_], Z[_]]
(forward: F ~> Z, inverse: Z ~> F)
(implicit zt: Traverse[Z])
: Traverse[F] = new Traverse[F] {
def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) ⇒ B): B = zt.foldLeft(forward(fa), b)(f)
def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
zt.foldRight(forward(fa), lb)(f)
def traverse[G[_], A, B]
(fa: F[A])
(f: (A) ⇒ G[B])
(implicit appG: Applicative[G])
: G[F[B]] = {
(zt.traverse(forward(fa))(f)(appG)).map(zb => inverse(zb))
}
}
// A little demo
def main(args: Array[String]): Unit = {
// To instantiate a `Traverse[Seq]`, we have to provide
// two natural transformations (from List to Seq and back):
implicit val seqTraverse: Traverse[Seq] = traverseFromIso(
new FunctionK[Seq, List] { def apply[X](sx: Seq[X]): List[X] = sx.toList },
new FunctionK[List, Seq] { def apply[X](lx: List[X]): Seq[X] = lx }
)
// do stuff with `Traversable[Seq]` here
}
}
从 cats 2.3 开始,现在内置了对 immutable.Seq
的支持。请参阅“Seq 的隐式实例在哪里?”在添加功能的 FAQ or this PR 上。