我有以下Scala程序:
object Test extends App {
val zip = (List(1, 3, 5), List(2, 4, 6)).zipped
val f: Tuple2[Int, Int] => Unit = x => println(x._1 + x._2)
zip.foreach(f)
}
为什么我收到以下编译器错误:
Error:(6, 15) type mismatch;
found : ((Int, Int)) => Unit
required: (Int, Int) => ?
zip.foreach(f)
当存在从Tuple2Zipped
到定义foreach[U](f: ((El1, El2)) => U): Unit
的Traversable
的隐式转换时。
注意:我编辑了我的问题以澄清。
我知道Tuple2Zipped
中定义的foreach
具有以下签名:
def foreach[U](f: (El1, El2) => U): Unit
并且我的函数f
不适合作为参数。
但是在Tuple2Zipped.scala中,特征ZippedTraversable2
和他的同伴对象是这样定义的:
trait ZippedTraversable2[+El1, +El2] extends Any {
def foreach[U](f: (El1, El2) => U): Unit
}
object ZippedTraversable2 {
implicit def zippedTraversable2ToTraversable[El1, El2](zz: ZippedTraversable2[El1, El2]): Traversable[(El1, El2)] = {
new scala.collection.AbstractTraversable[(El1, El2)] {
def foreach[U](f: ((El1, El2)) => U): Unit = zz foreach Function.untupled(f)
}
}
}
因此,由于我的函数f
确实与zippedTraversable2ToTraversable
返回的Traversable
中定义的foreach
参数匹配,并且配套对象定义了从ZippedTraversable2
到此Traversable
的隐式转换,并且Tuple2Zipped
是一个ZippedTraversable2
,我认为必须尝试这种转换并接受我的函数。
Intellij Idea 编辑器接受我的构造而不报告任何错误,但编译器失败并显示错误。
这是另一种稍微深奥的修复代码的方法。
zip.foreach(Function.untupled(f))
Function.untupled()
将采用一个((Int, Int)) => Unit
,这是你在f
中得到的,并返回一个(Int, Int) => Unit
,这是处理zip
元素所需要的。
你的函数 f 是错误的(错误中也有明确说明)
zip.foreach
期望Function2
采用类型T1
、键入T2
并返回R
,但您传递的Function1
Tuple2[Int,Int]
作为类型T1
以下是关于编译错误的解释:
found : ((Int, Int)) => Unit
等于Function1[Tuple2[Int,Int]] => Unit
而
required: (Int, Int) => Unit
等于Function2[Int,Int,Unit]
(请记住,()
是Tuple*
的句法糖,这就是为什么您看到双括号的原因)
下面是一个编译示例:
val zip = (List(1, 3, 5), List(2, 4, 6)).zipped
val f:Function2[Int,Int,Unit] = (x,y) => println(x + y)
zip.foreach[Unit](f)
你的对象是一个runtime.Tuple2Zipped
,如果你看签名
def foreach[U](f: (El1, El2) => U): Unit
它需要一个函数f: (El1, E1l2) => U
,但你的函数f: ((El1, El2)) => U
,因此错误。
我也发现它令人困惑,因为Tuple2Zipped
几乎看起来像一个List[(A,B)]
,实际上toList
方法会产生:
val zip = (List(1, 3, 5), List(2, 4, 6)).zipped.toList
val f: Tuple2[Int, Int] => Unit = x => println(x._1 + x._2)
zip.foreach(f)
编辑
我认为您正在寻找从((A, B)) => U
到(A, B) => U
的隐式转换,这不是zippedTraversable2ToTraversable
所做的。您可以定义如下内容:
implicit def tupconv[A,B] (f: (((A,B)) => Unit)): (A, B) => Unit = Function.untupled(f)
编辑 V2
对于上面的代码,您需要在作用域中有一个隐式变量,该变量将从((A,B)) => Unit
映射到(A, B) => Unit
,因为这是不匹配的地方。而你引用的那个确实ZippedTraversable2[El1, El2]
Traversable[(El1, El2)]
.如果要使用该隐式转换,可以执行以下操作:
val zip = (List(1, 3, 5), List(2, 4, 6)).zipped
val f: Tuple2[Int, Int] => Unit = x => println(x._1 + x._2)
def thisUsesTheImplicitYouWant(t: Traversable[(Int,Int)]) = t.foreach(f)
thisUsesTheImplicitYouWant(zip)
感谢 Jasper-M,我知道在这个问题上报告了一个 Scala 错误:https://github.com/scala/bug/issues/9523。这个错误是由Michael Pollmeier在一年半前提出的:为什么Scala隐式解析对于带有类型参数的重载方法失败?
Jamborta在这里发布了一个问题,其中包含一个更通用的示例。