在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)))
}
}
问题是:为什么会这样?我该如何解决?
答案 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
?