0

I have the following Scala program:

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

Why I get the following compiler error:

Error:(6, 15) type mismatch;
 found   : ((Int, Int)) => Unit
 required: (Int, Int) => ?
 zip.foreach(f)

when there is an implicit conversion from Tuple2Zipped to Traversable where foreach[U](f: ((El1, El2)) => U): Unit is defined.

NOTE: I edited my question to clarify.

I know that the foreach defined in Tuple2Zipped has the following signature:

def foreach[U](f: (El1, El2) => U): Unit

and that my function f does not fit as the argument.

But in Tuple2Zipped.scala is defined the trait ZippedTraversable2 and his companion object this way:

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

so as my function f does match the argument of the foreach defined in the Traversable returned by zippedTraversable2ToTraversable and the companion object defines an implicit conversion from ZippedTraversable2 to this Traversable and Tuple2Zipped is a ZippedTraversable2, i think that this conversion has to be tried and my function be accepted.

The Intellij Idea editor accepts my construct without reporting any error but the compiler fails with the shown error.

Josep M Beleta
  • 581
  • 4
  • 19
  • You are calling `.foreach` on `Tuple2Zipped`, such method exists, why would compiler apply conversion? http://docs.scala-lang.org/tutorials/tour/implicit-conversions (option #2). – Victor Moroz Apr 13 '17 at 01:09
  • But `foreach[U](f: ((El1, El2)) => U): Unit` is not a member of `Tuple2Zipped` but `Traversable` so the conversion is needed. – Josep M Beleta Apr 13 '17 at 11:31

4 Answers4

1

Here is yet another, and slightly more esoteric, way to fix your code.

zip.foreach(Function.untupled(f))

Function.untupled() will take a ((Int, Int)) => Unit, which is what you've got in f, and return a (Int, Int) => Unit, which is what you need to process the zip elements.

jwvh
  • 50,871
  • 7
  • 38
  • 64
  • Thank you for your answer but I edited my question to clarify it. The call to Function.untupled(f) is precisely what the foreach in ZippedTraversable2 does. – Josep M Beleta Apr 13 '17 at 08:00
0

Your function f is wrong ( it's also explicitly stated in the error )

zip.foreach expectes Function2 that takes type T1, type T2 and returns R, but you are passing a Function1 that takes Tuple2[Int,Int] as type T1

Here's an explaination regarding your compilation error :

found : ((Int, Int)) => Unit EQUALS Function1[Tuple2[Int,Int]] => Unit

Whereas

required: (Int, Int) => Unit EQUALS Function2[Int,Int,Unit]

(Keep in mind that () is syntactic sugar for Tuple* , that's why your are seeing the double parenthesis)

Here's an example that compiles :

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)
Sheinbergon
  • 2,875
  • 1
  • 15
  • 26
0

Your object is a runtime.Tuple2Zipped, if you look at the signature

def foreach[U](f: (El1, El2) => U): Unit

It takes a function f: (El1, E1l2) => U, but your function is f: ((El1, El2)) => U, hence the error.

I found it confusing too, as Tuple2Zipped almost looks like a List[(A,B)], in fact the toList method would produce just that:

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)

Edit

I think you are looking for an implicit conversion from ((A, B)) => U to (A, B) => U which is not what zippedTraversable2ToTraversable does. You can define something like this that would work:

implicit def tupconv[A,B] (f: (((A,B)) => Unit)): (A, B) => Unit = Function.untupled(f)

Edit V2

For the code above you'd need an implicit variable in scope that would map from ((A,B)) => Unit to (A, B) => Unit as that's where the mismatch is. Whereas the one you quoted does ZippedTraversable2[El1, El2] to Traversable[(El1, El2)]. If you'd like to make use of that implicit conversion, you could do this:

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)
jamborta
  • 5,130
  • 6
  • 35
  • 55
  • Thank you for your answer but I edited my question to clarify it. – Josep M Beleta Apr 13 '17 at 08:00
  • No, I am not looking for an implicit conversion from `((A, B)) => U to (A, B) => U`, what I am saying is that **there is** an implicit conversion from `ZippedTraversable2` to `Traversable` that has to be used when invoking a method, `foreach[U](f: ((El1, El2)) => U): Unit`, **that exists** in `Traversable` but not in `ZippedTraversable2` – Josep M Beleta Apr 13 '17 at 16:05
  • @jmbeleta I see what you mean. Updated the answer accordingly. – jamborta Apr 13 '17 at 17:01
  • 1
    `toTraversable` is already in the standard library. So this works, `zip.toTraversable.foreach(f)`, but it doesn't answer the question of why the implicit doesn't kick in. – jwvh Apr 13 '17 at 17:15
  • @jwvh yes, that's the point. Perhaps is a bug in the compiler. – Josep M Beleta Apr 13 '17 at 17:38
  • @jwvh Am I missing something? `zippedTraversable2ToTraversable` would only kick in if the compiler thinks `ZippedTraversable` needs to be converted to `Traversable`. – jamborta Apr 13 '17 at 18:22
  • @jamborta the implicit conversion is **already** in the scala library. It is in the companion object of a trait `ZippedTraversable2` that mixins `Tuple2Zipped` so it is in scope. This is why I think this should work with no further implicit conversions. In my example use for example `zip.size` and this compile with no problem and `size` is **not defined** in `Tuple2Zipped`. – Josep M Beleta Apr 13 '17 at 18:35
  • @jamborta and `Tuple2Zipped ` need to be converted to `ZippedTraversable2 ` in my case because `foreach[U](f: ((El1, El2)) => U): Unit` is not defined in `Tuple2Zipped` but in `Traversable`. – Josep M Beleta Apr 13 '17 at 18:40
  • @jmbeleta Interesting. Do you mind if I post a new question on this with a simplified example? I'm kind of curious what's going on. – jamborta Apr 13 '17 at 21:53
  • @jamborta fantastic, go on! – Josep M Beleta Apr 13 '17 at 22:23
  • @jamborta I said incorrectly that `foreach[U](f: ((El1, El2)) => U): Unit` is defined in `Traversable`.This is not true, is defined in the `Traversable` returned by the implicit conversion contained in `ZippedTraversable2`. – Josep M Beleta Apr 13 '17 at 22:29
0

Thanks to Jasper-M I have known that a Scala bug has been reported on this issue: https://github.com/scala/bug/issues/9523. This bug was reported by Michael Pollmeier that asked this question one and a half years ago: Why does Scala implicit resolution fail for overloaded method with type parameter?

jamborta posted a question with a more generic example on this same issue here.

Community
  • 1
  • 1
Josep M Beleta
  • 581
  • 4
  • 19