Scala:为什么使用单个参数函数的foreach不适用于压缩结果



我有以下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): UnitTraversable的隐式转换时。

注意:我编辑了我的问题以澄清。

我知道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,但您传递的Function1Tuple2[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在这里发布了一个问题,其中包含一个更通用的示例。

最新更新