具有较高类型的Scala .zip,行为怪异

时间:2019-05-24 19:33:53

标签: scala collections higher-kinded-types

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

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)))
  }

}

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

1 个答案:

答案 0 :(得分:1)

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

不幸的是,似乎没有任何隐含的东西能够构建例如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和两个CBF-一个用于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