Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala: Why does zipped method succeed with tuples of List and not Traversable?

Tags:

scala

I am attempting to zip two Traversables together in Scala. The following example does what I want using List instead of Traversable:

(List(1,2,3), List(3,4,5)).zipped.foreach( (a,b) => println(a,b) )

This code prints:

(1,3)
(2,4)
(3,5)

However when I attempt to do this with Traversable:

(Traversable(1,2,3), Traversable(3,4,5)).zipped.foreach( (a,b) => println(a,b) )

I get the following error:

8: error: 
No implicit view available from 
Traversable[Int] => scala.collection.IterableLike[El2,Repr2].

Can somebody explain what is going on with the above error? What is a view and how can I impliment the implicit view it wants?

like image 642
joshuaar Avatar asked Feb 28 '14 04:02

joshuaar


2 Answers

I've forgotten why Traversable is more general than Iterable -- maybe it has to do with the parallel collections, and maybe they've said it was a mistake, or a toss-up.

But you can just:

scala> (Traversable(1,2,3), Traversable(3,4,5).toIterable).zipped.foreach( (a,b) => println(a,b) )
(1,3)
(2,4)
(3,5)

The supplementary question is whether there is a downside to introducing an implicit to do that for you.

Edit: to answer your question, I guess it would look like the following, but this is not a recommendation:

scala> implicit def `convert it`[A](t: Traversable[A]): Iterable[A] = t.toIterable
warning: there were 1 feature warning(s); re-run with -feature for details
convert$u0020it: [A](t: Traversable[A])Iterable[A]

scala> (Traversable(1,2,3), Traversable(3,4,5)).zipped.foreach( (a,b) => println(a,b) )
(1,3)
(2,4)
(3,5)

A "view" in this sense is a conversion function that lets you "view" the Traversable as an Iterable and does not necessarily have to do with a "collection view".

Update:

A peek at the source, and only the second collection must be iterable.

The reason is that Tuple2Zipped will just foreach-traverse the first collection, but iterate the second so that it can stop when !hasNext.

There's interesting old discussion like:

http://www.scala-lang.org/old/node/2903.html

E.g., sample pull quote:

At least implicit in the contract of a Traversable is that you can traverse it multiple times.

The best ryule for a public interface is to return a traversable when you can. You can always turn it into am iterator if you need to.

And there is a question like this one that I think is mostly good for a laugh, though it is true enough.

like image 57
som-snytt Avatar answered Oct 20 '22 20:10

som-snytt


If you look at the full signature of zipped, you'll see that it looks like this:

def zipped[El1, Repr1, El2, Repr2](implicit w1: (T1) ⇒ TraversableLike[El1, Repr1], w2:  (T2) ⇒ IterableLike[El2, Repr2]): Tuple2Zipped[El1, Repr1, El2, Repr2]

This means that the function takes two implicit converters:

  • w1: (T1) ⇒ TraversableLike[El1, Repr1] converts from the type of the first element in the tuple (T1) to a TraversableLike
  • w2: (T2) ⇒ IterableLike[El2, Repr2] converts from the type of the second element (T2) to an IterableLike

In your case the problem is with the second one: there is no implicit converter in scope that could convert from Traversable to IterableLike. You could provide an implicit converter, but it is easier to just call toIterable on the second traversable:

(Traversable(1,2,3), Traversable(3,4,5).toIterable).zipped.foreach( (a,b) => println(a,b) )
like image 42
csgero Avatar answered Oct 20 '22 18:10

csgero