【问题标题】:Scala .zip with higher-kinded types, weird behaviourScala .zip 具有更高种类的类型,奇怪的行为
【发布时间】:2019-05-25 08:43:46
【问题描述】:

Scala methods and higher-kinded type parameters 中的问答之后,我询问了如何创建具有更高种类参数的方法,以便我可以使用任何 Seq,我现在在使用 .zip 方法时遇到了一个奇怪的问题。下面的代码无法编译,complire 报告:

Error:(18, 28) type mismatch;
 found   : Seq[X]
 required: S[X]
        itrA.zip(itrB).map {

我不知道 S[X] 在哪里转换为 Seq[X]。但是,如果我将调用 .zip 的代码替换为更简单的代码(已注释的代码),它简单地将 itrA 和 itrB 的两个头相加并返回 ItrA,则编译成功。

代码是:

object SeqOps {

  def sum[X: Numeric, S[Y] <: Seq[Y]](s: Seq[S[X]])
    (implicit cbf: CanBuildFrom[Nothing, X, S[X]]): Seq[X] =

  /*
      This code compiles
       s.reduce{(itrA, itrB) =>
          val num = implicitly[Numeric[X]]
          val sum = new num.Ops(itrA.head).+(itrB.head)
          itrA
          */

      s.reduce{(itrA, itrB) =>
        itrA.zip(itrB).map { // it seems that itrB loses the type here :/
          case (a, b) =>
            val num = implicitly[Numeric[X]]
            val sum = new num.Ops(a).+(b)
            sum
      }

  }

  def main(args: Array[String]): Unit = {
    sum(Seq(Vector(1), Vector(1)))
  }

}

问题是:为什么会这样?我该如何解决?

【问题讨论】:

    标签: scala collections higher-kinded-types


    【解决方案1】:

    它不知道如果你zip 两个S[X] 它可以以某种方式构建一个S[(X, X)]。为此,您需要知道如何构建S[(X, X)]CBF

    不幸的是,似乎没有任何隐式可以构建,例如Vector[Int] 来自通用 Seq[_],因此您需要有关您正在构建的集合类型的更多信息。这个想法是具有-Like 名称的特征可以提供此信息。在这种情况下,SeqLike[X, Repr] 带有 Repr 类型。

    如果你现在盯着signature of zip in SeqLike 看足够长的时间:

    def zip
      [A1 >: A, B, That]
      (that: GenIterable[B])
      (implicit bf: CanBuildFrom[Repr, (A1, B), That])
    : That
    

    然后你会看到你可以给它一个CBF,它在第一个组件中有Repr。所以你可以尝试这样的事情(注意with SeqLike 和两个CBFs - 一个用于zip,另一个用于map):

    import scala.collection.generic.CanBuildFrom
    import scala.language.higherKinds
    import scala.collection.SeqLike
    
    object SeqOps {
    
    
      def sum[X: Numeric, S[Y] <: Seq[Y] with SeqLike[Y, S[Y]]]
        (s: Seq[S[X]])
        (implicit 
          cbf1: CanBuildFrom[S[_], X, S[X]],
          cbf2: CanBuildFrom[S[_], (X, X), S[(X, X)]]
        )
      : Seq[X] = {
        val num = implicitly[Numeric[X]]
        import num._
        s.reduce(_.zip(_)(cbf2).map{ case (a, b) => a + b })
      }
    
    
      def main(args: Array[String]): Unit = {
        println(sum(Seq(Vector(1), Vector(1))))     // Vector(2)
        println(sum(Seq(List(2, 3), List(4, 5))))   // List(6, 8)
      }
    
    }
    

    另外说明:在某些情况下,将Nothing 视为Unobtanium 更容易。如果您的CBF 需要Unobtanium 来构建S[X],这可能对任何事情都没有好处,因为您想从哪里获得Unobtanium

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-12-09
      • 2021-01-05
      • 2019-05-25
      • 1970-01-01
      • 1970-01-01
      • 2015-08-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多