【问题标题】:Scala cats, traverse SeqScala猫,遍历Seq
【发布时间】:2021-08-18 23:04:05
【问题描述】:

我知道我可以遍历Lists

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] 实例的简单方法?

【问题讨论】:

  • 请问你为什么要不断地将Seq 包装和解包到List 并返回,而不是将所有内容都转换为List 然后再使用它?
  • 在我正在处理的项目中,Seq 到处都在使用,不知道为什么。也许我们可以改用List

标签: scala traversal scala-cats


【解决方案1】:

Cats 不提供 Seq 的类型类实例,因此除了自己实现之外,您还无法进行转换。

至于为什么,一个(有点老的)Cats issue 正在进行讨论。总而言之,您对 Seq 的基本特征了解不够,无法确保某些类型类实例的法律成立。

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

【讨论】:

    【解决方案2】:

    从 cat 2.3 开始,现在内置了对 immutable.Seq 的支持。请参阅“Seq 的隐式实例在哪里?”在添加功能的FAQthis PR 上。

    【讨论】:

      【解决方案3】:

      如果您绝对确定从所有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 }
       )
      

      完整代码 sn-p(使用 scala 2.12.4 和 cat 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
        }
      }    
      

      【讨论】:

        猜你喜欢
        • 2019-06-05
        • 2023-03-26
        • 1970-01-01
        • 2017-07-13
        • 2019-02-12
        • 2014-10-26
        • 1970-01-01
        • 1970-01-01
        • 2016-12-23
        相关资源
        最近更新 更多