6

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?

joshuaar
  • 87
  • 1
  • 7

2 Answers2

5

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.

Community
  • 1
  • 1
som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • Thanks, I guess my question comes down to the difference between traversals and iterables and whether there is any downside to just converting to an iterable. – joshuaar Feb 28 '14 at 07:38
  • There's no downside to the first example. You need an iterator at some point. I'll edit. – som-snytt Feb 28 '14 at 08:03
1

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) )
csgero
  • 2,753
  • 17
  • 15
  • Thanks, that is very clear, though I still don't understand why the second element needs to be an iterable. I get that it needs it because the signature says so, but is it possible to implement a zip operation on two Traversables? Would there be any reason to do so? – joshuaar Feb 28 '14 at 16:19
  • @joshuaar The reason is that `Tuple2Zipped` will just `foreach`-traverse the first collection, but iterate the second so that it can stop when `!hasNext`. – som-snytt Feb 28 '14 at 17:40
  • Yes, that's correct. You can check out https://github.com/scala/scala/blob/v2.10.3/src/library/scala/runtime/Tuple2Zipped.scala for all the nitty-gritty details. – csgero Feb 28 '14 at 17:50
  • got it, all makes sense now. Thanks for the links. – joshuaar Feb 28 '14 at 21:33