Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Working with tuples in Scala

Tags:

tuples

scala

I want to do something like this (simplified quite heavily):

((1, 2, 3, 4, 5, 6), (6, 5, 4, 3, 2, 1)).zipped map (_ + _)

Ignore the actual values of the integers (although it's important that these are 6-tuples, actually :)). Essentially, I want to use this fairly regularly in a function which maintains a Map[String, (Int, Int, Int, Int, Int, Int)] when an existing element is updated.

As it is, Scala spits this out at me:

<console>:6: error: could not find implicit value for parameter w1: ((Int, Int, Int, Int, Int, Int)) => scala.collection.TraversableLike[El1,Repr1]
   ((1, 2, 3, 4, 5, 6), (6, 5, 4, 3, 2, 1)).zipped

If I use Seqs instead of tuples, everything works fine, but I want to enforce an arity of 6 in the type system (I'll probably type Record = (Int, Int, Int, Int, Int, Int) as a quick refactor shortly).

Can anyone offer some advice on what I'm doing wrong/why Scala won't deal with the code above? I thought it might work if I used a 2- or 3-arity tuple, seeing as Scala defines Tuple2 and Tuple3s (I understand that scaling tuple functions across an arbitrary n-arity is difficult), but I get the same error.

Thanks in advance for any help offered :).

like image 615
frio Avatar asked Oct 12 '10 23:10

frio


2 Answers

You only want to map over tuples which have identical types--otherwise the map wouldn't make sense--but Tuple doesn't contain that in its type signature. But if you're willing to do a little work, you can set it up so that tuples work the way you requested:

Groundwork:

class TupTup6[A,B](a: (A,A,A,A,A,A), b: (B,B,B,B,B,B)) {
  def op[C](f:(A,B)=>C) = ( f(a._1,b._1), f(a._2,b._2), f(a._3,b._3), 
                            f(a._4,b._4), f(a._5,b._5), f(a._6,b._6) )
}
implicit def enable_tuptup6[A,B](ab: ((A,A,A,A,A,A),(B,B,B,B,B,B))) = {
  new TupTup6(ab._1,ab._2)
}

Usage:

scala> ((1,2,3,4,5,6) , (6,5,4,3,2,1)) op { _ + _ }
res0: (Int, Int, Int, Int, Int, Int) = (7,7,7,7,7,7)
like image 189
Rex Kerr Avatar answered Nov 15 '22 10:11

Rex Kerr


I received this little inspiration.

class TupleZipper[T <: Product](t1: T) {
  private def listify(p: Product) = p.productIterator.toList
  def zipWith(t2: T) = (listify(t1), listify(t2)).zipped
}
implicit def mkZipper[T <: Product](t1: T) = new TupleZipper(t1)

// ha ha, it's arity magic
scala> ((1, 2, 3, 4, 5, 6)) zipWith ((6, 5, 4, 3, 2))                      
<console>:8: error: type mismatch;
 found   : (Int, Int, Int, Int, Int)
 required: (Int, Int, Int, Int, Int, Int)
       ((1, 2, 3, 4, 5, 6)) zipWith ((6, 5, 4, 3, 2))
                                     ^

scala> ((1, 2, 3, 4, 5, 6)) zipWith ((6, 5, 4, 3, 2, 1))                   
res1: (List[Any], List[Any])#Zipped[List[Any],Any,List[Any],Any] = scala.Tuple2$Zipped@42e934e

scala> res1 map ((x, y) => x.asInstanceOf[Int] + y.asInstanceOf[Int])      
res2: List[Int] = List(7, 7, 7, 7, 7, 7)

Yes, a bunch of Anys comes out the other end. Not real thrilling but not a lot you can do when you try to force yourself on Tuples this way.

Edit: oh, and of course the type system gives you the full monty here.

scala> ((1, 2, 3, 4, 5, 6)) zipWith ((6, 5, 4, 3, 2, "abc"))         
<console>:8: error: type mismatch;
 found   : java.lang.String("abc")
 required: Int
       ((1, 2, 3, 4, 5, 6)) zipWith ((6, 5, 4, 3, 2, "abc"))
                                                     ^
like image 21
psp Avatar answered Nov 15 '22 11:11

psp