(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