(1,2,3)
better than List(1,2,3)
. With the Tuple you cannot do the maps, filters, etc... (with good reason) but there is a use-case for being able to convert a Tuple to a Traversable. Word of warning. Unlike collections Tuples are very often not homogeneous. IE you do not have
Tuple2[Int]
you have Tuple2[A,B]
. So the best you can do is to map a tuple to an Iterator[Any]. Note: this was done with Scala 2.8 so results may be slightly different with 2.7. But I believe the syntax is valid.
- // the productIterator method gives access to an iterator over the elements
- scala> (1,2,3).productIterator.map {_.toString} mkString (",")
- res1: String = 1,2,3
- scala> (1,2,3).productIterator foreach {println _}
- 1
- 2
- 3
- // if you know the tuple is heterogeneous then you can use partial functions
- // for casting the elements to a particular type.
- scala> (1,2,3).productIterator map {case i:Int => i + 2} foreach {println _}
- 3
- 4
- 5
- // To get a full Traversable out of the deal you use one of the many
- // to* methods to convert to Seq, List, Array, etc...
- scala> (1,2,3).productIterator.toList map {case i:Int => i + 2}
- res15: List[Int] = List(3, 4, 5)
- // and if you want you can use an implicit to clean up the syntax a bit
- // Problem with this is you need an implicit for each Tuple length
- scala> implicit def tupleToTraversable[T](t:(T,T,T)) = t.productIterator.toList map { case e:T => e}
- warning: there were unchecked warnings; re-run with -unchecked for details
- tupleToTraversable: [T](t: (T, T, T))List[T]
- scala> (1,2,3) foreach {println _}
- 1
- 2
- 3
- /*
- EDIT: Dan pointed out that the methods I am using are inherited from the
- Product super class of Tuple. So you can do something similar as follows.
- Note: I am using the same name as the previous method so that they don't interfer
- with one another
- */
- scala> implicit def tupleToTraversable[T](t:Product) = t.productIterator.toList map { case e:T => e}
- warning: there were unchecked warnings; re-run with -unchecked for details
- tupleToTraversable: [T](t: Product)List[T]
- scala> (1,2,3) foreach {println _}
- 1
- 2
- 3
- // this is interesting... it does cast to int unless required
- scala> (1,2,'t') foreach {println _}
- 1
- 2
- t
- // lets verify we are getting correct conversion
- scala> def inc(l:List[Int]) = l map {_ + 1}
- inc: (l: List[Int])List[Int]
- scala> inc ( (1,2,3))
- res4: List[Int] = List(2, 3, 4)
- // yep this I expected
- scala> inc ( (1,2,'t'))
- java.lang.ClassCastException: java.lang.Character cannot be cast to java.lang.Integer
- at scala.runtime.BoxesRunTime.unboxToInt(Unknown Source)
- at $anonfun$inc$1.apply(< console>:7)
- at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:238)
- at .< clinit>(< console>)
- scala> def inc(l:List[Int]) = l foreach {println _}
- inc: (l: List[Int])Unit
- scala> def p(l:List[Int]) = l foreach {println _}
- p: (l: List[Int])Unit
- scala> p ( (1,2,'t'))
- 1
- 2
- t
Couldn't you get something similar for all tuples by using Product as the parameter type instead of the individual tuple types? All of the methods that you are using on a tuple are inherited from the Product type.
ReplyDeleteThe truly sad thing is that I have even done that in one of my project but forgot when doing this post. I will update this post as soon as I am back at the computer
ReplyDeletethanks
I vote for an entire post devoted to Product since... I don't know what that is :-)
ReplyDelete@Dan On second thought there is a small difference from what I was doing here and just using Product. If i go the product route I can't statically verify that all elements of the tuple are of the same type.
ReplyDeleteI am updating the post to reflect the difference.
@HandyMan: Topic added for the future.
ReplyDelete