"update" method with variable-length index in Scala - scala

I was writing code for something like an array with variable dimensions. What I do is to maintain a linear underlying collections and wrap it up with index access methods. Since the dimension of the data structure is not known, I write something like
def apply(i: Int*): Double = ...
And it works perfectly. However, I cannot do the same thing to update 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 fine but not what I really want. I guess the problem about update may be fixed in two ways:
Change the order of arguments in update function, which makes it look weird.
Allow variable-length arguments not as the last one. I find this questions asked in a general setting and it can be solved by using currying functions, which does not apply here.
The problem about += seems more complicated and it even exists when the index is of fixed length. Maybe we can add an object that has += operator and use this(...) to get the object (so that this(...) += v will invoke some method as we expect), but that will conflict with the apply method.
If anyone has solution to any of the above questions or has a reason why we shouldn't be able to write code like this, please share your ideas! Thanks~

update is a fairly peculiar artefact in Scala because it is mainly syntactic sugar and doesn't correspond to any particular method signature. This means that we can be creative and give update an arity-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)
As it stands, this leaves the types of the indices on the LHS and the type of the value on the RHS completely unconstrained,
scala> i(23, true, 'c') = "foo"
res4: (Int, Boolean, Char, String) = (23,true,c,foo)
but we can fix that with some implicit evidence provided by the new support for tuples 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
^

The simplest solution I see right now is to have many different overloads of update for every dimension that you want to support. Say that you can determine that the maximum dimension that you'll ever use is 10, this means that you'll need 10 overloads. This might not seem very practical, but i can easily be abstracted away so it is very much practical actually:
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 ...
}
Usage:
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 test in the 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

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
Can't do that trick with update itself, however. Might be worth filing an enhancement request.

Related

Represent T* as concrete type

(T,T)* resolves to Seq[(T,T)] after erasure, but how to represent (T,T)* itself as a type?
The reason I ask is there's an API I'm using that defines a:
def foo(bar: (String,String)*) = ...
but fails when I try to pass in a Seq[(String,String)].
My pull request to add in:
def foo(bar: Seq[(String,String)]) = ...
blows up due to the 2 methods having the same type after erasure.
Are star projections able to be represented as a concrete type?
You can pass the Seq if you follow it with :_* like this:
val s:Seq[(String, String)] = Seq( ("a", "b"), ("c", "d"), ... )
foo(s:_*)
So you shouldn't need both signatures.
To disambiguate the erased signatures:
scala> class X { def f(is: Int*) = is.sum }
defined class X
scala> class Y extends X { def f(is: Seq[Int])(implicit d: DummyImplicit): Int = f(is: _*) }
defined class Y
scala> new Y().f(1 to 10)
res3: Int = 55
or this is better, the signatures in collections always look like this to mean "two or more":
scala> class X {
| def f(i: Int): Int = i
| def f(is: Seq[Int]): Int = is.sum
| def f(i: Int, j: Int, rest: Int *): Int = i + j + rest.sum
| }
defined class X
scala> new X().f(3)
res9: Int = 3
scala> new X().f(3,4)
res10: Int = 7
scala> new X().f(3,4,5)
res11: Int = 12
scala> new X().f(1 to 10)
res12: Int = 55
You can't refer to a repeated parameter type, just as you can't refer to a by-name parameter type as such. So you can't convert to it. However, you can detect it reflectively, by name:
scala> import reflect.runtime.universe._
import reflect.runtime.universe._
scala> typeOf[X].member(TermName("f")).asMethod.paramss.flatten.head.asTerm.typeSignature.typeSymbol.name
warning: there were 1 deprecation warning(s); re-run with -deprecation for details
res4: reflect.runtime.universe.Symbol#NameType = <repeated>
There is internal API, definitions.isRepeated(sym), if you want to cast for it.

Implicit conversion for multiple parameters

Is it possible to implement in Scala an implicit conversion for the group of parameters (without defining them as some class member) like
implicit def triple2One (x :Int, s :String, d :Double) = x // just as an example
So that I would be able to call it in the code like
val x :Int = (1, "test", 2.0)
It is possible:
scala> implicit def iFromISD(isd: (Int, String, Double)): Int = isd._1
iFromISD: (isd: (Int, String, Double))Int
scala> val x: Int = (1, "two", 3.0)
x: Int = 1
Naturally, there has to be a type annotation on the resulting val to drive the search for and application of the implicit conversion.
Addendum
It occurs to me there's another way that doesn't involve dubious implicit conversions:
scala> val (y, _, _) = (1, "two", 3.0)
y: Int = 1

Problems with contravariance in Scala

I want to define a type-class like this:
trait CanFold[-T, R] {
def sum(acc: R, elem: T): R
def zero: R
}
implicit object CanFoldInts extends CanFold[Int, Int] {
def sum(x: Int, y: Int) = x + y
def zero = 0
}
implicit object CanFoldSeqs extends CanFold[Traversable[_], Traversable[_]] {
def sum(x: Traversable[_], y: Traversable[_]) = x ++ y
def zero = Traversable()
}
def sum[A, B](list: Traversable[A])(implicit adder: CanFold[A, B]): B =
list.foldLeft(adder.zero)((acc,e) => adder.sum(acc, e))
However, the problem is when I do this I get a Traversable[Any] and it
would be nice to get a Traversable[Int] instead:
scala> sum(List(1,2,3) :: List(4, 5) :: Nil)
res10: Traversable[Any] = List(1, 2, 3, 4, 5)
To make matters worse, I cannot define an implicit for
Traversable[Int] after defining one for Traversable[_], because then
the definitions would cause ambiguity. After pulling my hair out I
gave up.
Is there any way I could make that sum return a
Traversable[T] instead of a Traversable[Any]?
Looking at how sum() is defined on Seq in Scala's library, I can see it works with Numeric, which is invariant, but I want default implementations for supertypes and having the result be different than the input (same as the fold operation) is nice.
The only way I know to add type parameters to such type classes is to use a def instead of an object:
implicit def CanFoldSeqs[A] = new CanFold[Traversable[A], Traversable[A]] {
def sum(x: Traversable[A], y: Traversable[A]) = x ++ y
def zero = Traversable()
}
scala> sum(List(1, 2, 3) :: List(4, 5) :: Nil)
res0: Traversable[Int] = List(1, 2, 3, 4, 5)

Scala, Currying on multi parameter-group method including implicit params?

After having discovered that currying multi parameter-groups method is possible, I am trying to get a partially applied function which requires implicit parameters.
It seams not possible to do so. If not could you explain me why ?
scala> def sum(a: Int)(implicit b: Int): Int = { a+b }
sum: (a: Int)(implicit b: Int)Int
scala> sum(3)(4)
res12: Int = 7
scala> val partFunc2 = sum _
<console>:8: error: could not find implicit value for parameter b: Int
val partFunc2 = sum _
^
I use a singleton object to create this partially applied function and I want to use it in a scope where the implicit int is defined.
That is because you don't have an implicit Int in scope. See:
scala> def foo(x: Int)(implicit y: Int) = x + y
foo: (x: Int)(implicit y: Int)Int
scala> foo _
<console>:9: error: could not find implicit value for parameter y: Int
foo _
^
scala> implicit val b = 2
b: Int = 2
scala> foo _
res1: Int => Int = <function1>
The implicit gets replaced with a real value by the compiler. If you curry the method the result is a function and functions can't have implicit parameters, so the compiler has to insert the value at the time you curry the method.
edit:
For your use case, why don't you try something like:
object Foo {
def partialSum(implicit x: Int) = sum(3)(x)
}
scala> object MySingleton {
| def sum(a: Int)(implicit b: Int): Int = { a+b }
|
|
| def caller(a: Int) = {
| implicit val b = 3; // This allows you to define the partial below
| def pf = sum _ // and call sum()() without repeating the arg list.
| pf.apply(a)
| }
| }
defined module MySingleton
scala> MySingleton.caller(10)
res10: Int = 13

scala tuple unpacking

I know this question has come up many times in different ways. But it is still not clear to me. Is there a way to achieve the following.
def foo(a:Int, b:Int) = {}
foo(a,b) //right way to invoke foo
foo(getParams) // is there a way to get this working without explicitly unpacking the tuple??
def getParams = {
//Some calculations
(a,b) //where a & b are Int
}
It's a two step procedure. First turn foo into a function, then call tupled on it to make it a function of a tuple.
(foo _).tupled(getParams)
#dave-griffith is dead on.
You can also call:
Function.tupled(foo _)
If you want to wander into "way more information than I asked for" territory, there are also methods built into partially applied functions (and on Function) for currying. A few input/output examples:
scala> def foo(x: Int, y: Double) = x * y
foo: (x: Int,y: Double)Double
scala> foo _
res0: (Int, Double) => Double = <function2>
scala> foo _ tupled
res1: ((Int, Double)) => Double = <function1>
scala> foo _ curried
res2: (Int) => (Double) => Double = <function1>
scala> Function.tupled(foo _)
res3: ((Int, Double)) => Double = <function1>
// Function.curried is deprecated
scala> Function.curried(foo _)
warning: there were deprecation warnings; re-run with -deprecation for details
res6: (Int) => (Double) => Double = <function1>
Wherein the curried version is invoked with multiple argument lists:
scala> val c = foo _ curried
c: (Int) => (Double) => Double = <function1>
scala> c(5)
res13: (Double) => Double = <function1>
scala> c(5)(10)
res14: Double = 50.0
Finally, you can also uncurry/untuple if needed. Function has builtins for this:
scala> val f = foo _ tupled
f: ((Int, Double)) => Double = <function1>
scala> val c = foo _ curried
c: (Int) => (Double) => Double = <function1>
scala> Function.uncurried(c)
res9: (Int, Double) => Double = <function2>
scala> Function.untupled(f)
res12: (Int, Double) => Double = <function2>
Function.tupled(foo _)(getParams) or the one suggested by Dave.
EDIT:
To respond to your comment:
What if foo happens to be the
constructor of some class?
In that case, this trick won't work.
You can write a factory method in the companion object of your class and then obtain the tupled version of its apply method using one of the aforementioned techniques.
scala> class Person(firstName: String, lastName: String) {
| override def toString = firstName + " " + lastName
| }
defined class Person
scala> object Person {
| def apply(firstName: String, lastName: String) = new Person(firstName, lastName)
| }
defined module Person
scala> (Person.apply _).tupled(("Rahul", "G"))
res17: Person = Rahul G
With case classes you get a companion object with an apply method for free, and thus this technique is more convenient to use with case classes.
scala> case class Person(firstName: String, lastName: String)
defined class Person
scala> Person.tupled(("Rahul", "G"))
res18: Person = Person(Rahul,G)
I know that's a lot of code duplication but alas... we don't have macros (yet)! ;)
I appreciate some of the other answers which were closer to what you asked for, but I found it easier for a current project to add another function which converts tuple parameters into the split parameters:
def originalFunc(a: A, b: B): C = ...
def wrapperFunc(ab: (A, B)): C = (originalFunc _).tupled(ab)
Now, you can implement foo and make it take a param of the Tuple2 class like so.
def foo(t: Tuple2[Int, Int]) = {
println("Hello " + t._1 + t._2)
"Makes no sense but ok!"
}
def getParams = {
//Some calculations
val a = 1;
val b = 2;
(a, b) //where a & b are Int
}
// So you can do this!
foo(getParams)
// With that said, you can also do this!
foo(1, 3)