繁体   English   中英

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提供类型类实例,因此除了自己实现之外,您还需要进行转换。

至于为什么,在(有点旧的)猫问题中有一个正在进行的讨论。 总而言之,您对Seq底层特性了解得不够多,无法确保某些类型类实例法则成立。

编辑:没关系,它现在存在,请参阅链接线程

如果您完全确定在您的代码中从所有SeqList的转换将始终成功,您可以简单地通过(伪)同构将Traverse结构从List转移到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))
    }
  }

这并不是真正的同构,因为从SeqList的转换可能会失败(例如,如果序列是无限的)。 它所做的只是简单地将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 和猫 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 的隐式实例在哪里?” 在添加功能的常见问题解答此 PR上。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM