0

In scala, we can get an iterator over a tuple as follows

val t = (1, 2)
val it = t.productIterator

and even

it.foreach( x => println(x.isInstanceOf[Int]) )

returns true, we cannot do simple operations on the iterator values without using asInstanceOf[Int], since

it.foreach( x => println(x+1) )

returns an error: type mismatch; found : Int(1) required: String

I understand the issue with Integer vs. Int, but still the validity of isInstanceOf[Int] is somewhat confusing.

What is the best way to do these operations over tuples? Notice that the tuple can have a mix of types like integers with doubles, so converting to a list might not always work.

deepkimo
  • 3,187
  • 3
  • 23
  • 21
  • that question seems more about flattening a tuple of tuples – deepkimo Sep 07 '13 at 05:41
  • 1
    if you find yourself wanting to iterate over a tuple and apply a function to each value, it's generally a sign that you shouldn't be using a tuple in the first place. Tuples are not collections and you shouldn't try to use them as such. – Luigi Plinge Sep 07 '13 at 08:06
  • Right, but sometimes you get tuples anyway like when reading from a database, and the simplest is to deal with them directly. – deepkimo Sep 07 '13 at 19:32
  • Well, it might not be simplest after all ;-). – Blaisorblade Oct 28 '13 at 01:38

2 Answers2

5

A tuple does not have to be homogenous and the compiler didn't try to apply magic type unification across the elements1. Take (1, "hello") as an example of such a a heterogenous tuple (Tuple2[Int,String]).

This means that x is typed as Any (not Int!). Try it.foreach( (x: Int) => println(x) ), with the original tuple, to get a better error message indicating that the iterator is not unified over the types of the tuple elements (it is an Iterators[Any]). The error reported should be similar to:

error: type mismatch;
 found   : (Int) => Unit
 required: (Any) => ?
       (1, 2).productIterator.foreach( (x: Int) => println(x) )

In this particular case isInstanceOf[Int] can be used to refine the type - from the Any that the type-system gave us - because we know, from manual code inspection, that it will "be safe" with the given tuple.

Here is another look at the iterators/types involved:

(1, 2)                         // -> Tuple2[Int,Int]
  .productIterator             // -> Iterator[Any]
  .map(_.asInstanceOf[Int])    // -> Iterator[Int]
  .foreach(x => println(x+1))

While I would recommend treating tuples as finite sets of homogenous elements and not a sequence, the same rules can be used as when dealing with any Iterator[Any] such as using pattern matching (e.g. match) that discriminates by the actual object type. (In this case the code is using an implicit PartialFunction.)

(1, "hello").productIterator
  .foreach {
    case s: String => println("string: " + s)
    case i: Int => println("int: " + i)
  }

1 While it might be possible to make the compiler unify the types in this scenario, it sounds like a special case that requires extra work for minimal gain. Normally sequences like lists - not tuples - are used for homogenous elements and the compiler/type-system correctly gives us a good refinement for something like List(1,2) (which is typed as List[Int] as expected).

user2246674
  • 7,621
  • 25
  • 28
  • Thanks, I like the way you are doing the pattern matching to deal with this. – deepkimo Sep 07 '13 at 05:40
  • Pattern matching is indeed better style than `isInstanceOf` - that's why the latter has (on purpose) such an long and ugly name. It's better style because it unifies `isInstanceOf` and `asInstanceOf` in a less error-prone way. – Blaisorblade Oct 28 '13 at 01:39
  • the tuple's `productIterator` using `each` and `case` do the magic! Thanks @user2246674 – Peter Krauss Nov 12 '19 at 17:02
1

There is another type HList, that is like tuple and List all-in-one. See shapeless.

I think, you can get close to what you want:

import shapeless._
val t = 1 :: 2 :: HNil
val lst = t.toList
lst.foreach( x => println(x+1) )
Arseniy Zhizhelev
  • 2,381
  • 17
  • 21
  • If you already have a tuple, can you convert to HList directly without having to do the manual HList building? – deepkimo Sep 07 '13 at 19:34
  • I don't know. On stackoverflow there are plenty of HList questions (see http://stackoverflow.com/questions/tagged/shapeless, http://stackoverflow.com/questions/11825129/are-hlists-nothing-more-than-a-convoluted-way-of-writing-tuples?rq=1 for instance). – Arseniy Zhizhelev Sep 07 '13 at 19:50
  • I found: https://github.com/milessabin/shapeless/blob/master/examples/src/main/scala/shapeless/examples/flatten.scala#L70 Yes you can convert tuples to HLists and vise versa. – Arseniy Zhizhelev Sep 07 '13 at 20:08
  • @deepkimo: if you like some answer, make sure to accept it. – Blaisorblade Oct 28 '13 at 01:38