"update" with variable length index in Scala

I wrote code for something like an array with variable sizes. What I am doing is maintaining linear base collections and wrapping them with index access methods. Since the size of the data structure is unknown, I am writing something like

def apply(i: Int*): Double = ... 

And it works great. However, I cannot do the same to update the method and operators like + =, so I end up writing methods like

 def set(v: Double, i: Int*) ... def add(v: Double, i: Int*) ... 

which is beautiful, but not what I really want. I think the update problem can be fixed in two ways:

  • Reorder the arguments in the update function, which makes it weird.
  • Allow arguments of variable length not as last. I ask this question in general setup, and it can be solved using currying functions that are not applicable here.

The + = problem seems more complicated and even exists when the index has a fixed length. Perhaps we can add an object that has the + = operator and use this (...) to get the object (so this (...) + = v will call some method, as we expect), but this will conflict with apply method.

If someone has a solution to any of the above issues, or if there is a reason why we cannot write such a code, please share your ideas! Thanks ~

+6
source share
3 answers

The simplest solution that I see now is to have many different update overloads for each dimension that you want to support. Say that you can determine that the maximum size you ever use is 10, which means you will need 10 overloads. This may not seem very practical, but I can easily get distracted, so it's practically practical:

 trait MultiKeyUpdate[K, V] { def doUpdate( k: K* )( v: V ) def update(k1: K, v: V) { doUpdate( k1 )( v ) } def update(k1: K, k2: K, v: V) { doUpdate( k1, k2 )( v ) } def update(k1: K, k2: K, k3: K, v: V) { doUpdate( k1, k2, k3 )( v ) } // ... and so on, up until max dimension ... } 

Using:

 class C extends MultiKeyUpdate[Int, Double] { def apply(i: Int*): Double = { println("Returning element " + i.mkString("[",",","]")) 123 } def doUpdate( i: Int* )( v: Double ) { println("Updating element " + i.mkString("[",",","]") + " to value " + v) } } 

And some tests in REPL:

 scala> val o = new C o: C = C@12798c1 scala> o(1,2,3) Returning element [1,2,3] res3: Double = 123.0 scala> o(1,2,3) = 5.0 Updating element [1,2,3] to value 5.0 scala> o(1,2,3) += 7.0 Returning element [1,2,3] Updating element [1,2,3] to value 130.0 
+2
source

update is a rather peculiar artifact in Scala, because it is basically syntactic sugar and does not correspond to any specific method signature. This means that we can be creative and update the arterial polymorphic signature,

 scala> class Indexed { def update[P <: Product](p: P) = p } defined class Indexed scala> val i = new Indexed i: Indexed = Indexed@1ea0e836 scala> i(0) = 1.0 res0: (Int, Double) = (0,1.0) scala> i(0, 1) = 1.0 res1: (Int, Int, Double) = (0,1,1.0) scala> i(0, 1, 2) = 1.0 res2: (Int, Int, Int, Double) = (0,1,2,1.0) scala> i(0, 1, 2, 3) = 1.0 res3: (Int, Int, Int, Int, Double) = (0,1,2,3,1.0) 

In its current form, this leaves the index types on LHS and the value type on RHS is completely unlimited,

 scala> i(23, true, 'c') = "foo" res4: (Int, Boolean, Char, String) = (23,true,c,foo) 

but we can fix this with some implicit evidence provided by the new tuple support in shapeless 2.0.0-SNAPSHOT,

 scala> import shapeless._ import shapeless._ scala> import syntax.tuple._ import syntax.tuple._ scala> class Indexed { | def update[P <: Product, I](p: P) | (implicit | init: TupleInit.Aux[P, I], | toList: TupleToList[I, Int], | last: TupleLast.Aux[P, Double]) = (toList(init(p)), last(p)) | } defined class Indexed scala> val i = new Indexed i: Indexed = Indexed@76ab909a scala> i(0) = 1.0 res10: (List[Int], Double) = (List(0),1.0) scala> i(0, 1) = 2.0 res11: (List[Int], Double) = (List(0, 1),2.0) scala> i(0, 1, 2) = 3.0 res12: (List[Int], Double) = (List(0, 1, 2),3.0) scala> i(0, 1, 2, 3) = 4.0 res13: (List[Int], Double) = (List(0, 1, 2, 3),4.0) scala> i(0, 1, 2) = "foo" // Doesn't compile <console>:22: error: could not find implicit value for parameter last: shapeless.ops.tuple.TupleLast.Aux[(Int, Int, Int, String),Double] i(0, 1, 2) = "foo" // Doesn't compile ^ scala> i(23, "foo", true) = 5.0 // Doesn't compile <console>:22: error: could not find implicit value for parameter toList: shapeless.ops.tuple.TupleToList[I,Int] i(23, "foo", true) = 5.0 // Doesn't compile ^ 
+5
source
 class SetMe { def set(i: Int*)(v: Double) { println("Set "+v+" with "+i.mkString(",")) } } scala> (new SetMe).set(4,7,19,3)(math.Pi) Set 3.141592653589793 with 4,7,19,3 

It is not possible to perform this trick with update itself. It might be worth submitting a promotion request.

+1
source

All Articles