Implicit conversion for multiple parameters - scala

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

Related

Return a sequence with dynamic type in scala

I'm trying to define a function, which should return a range from 1 to n of type T. Here, T is given as user-input and an implicit def method is available to convert int to T. I.e., what I would like to have, but doesn't work, is:
def createSeq[T](n: Int): Seq[T] =
1 to n map _.asInstanceOf[T]
For example, I can call createSeq[Int](3), which works like a charm. However, when I call createSeq[String](3), it still returns Vector(1,2,3), which contains integers and not strings.
Let's define createSeq, asking for an implicit conversion Int => T from Int to T, as:
def createSeq[T](n: Int)(implicit toT: Int => T): Seq[T] =
1.to(n).map(toT)
Then you can do:
scala> createSeq[Int](3)
res: Seq[Int] = Vector(1, 2, 3)
Let's add an implicit conversion Int => String:
implicit def toStr(n: Int): String = n.toString
Then you can do:
scala> createSeq[String](3)
res: Seq[String] = Vector(1, 2, 3)
Note: this last Vector(1, 2, 3) is of type Seq[String], it is actually Vector("1", "2", "3") but " are omitted.

Applying implicit conversion to map

I tried implicit conversions in the following example:
val m: Map[Int, Int] = Map(10 -> "asd") //fine
val mm: Map[Int, Int] = Map("asd" -> 20) //type mismatch; found: (String, Int)
//required: (Int, Int)
implicit def stringToInt(str: String): Int = 10
Why can't we apply implicit conversions to map keys? Is there a way to work around this?
It doesn't work because you're using -> which is an (inline) operator:
implicit final class ArrowAssoc[A](self : A) extends scala.AnyVal {
#scala.inline
def ->[B](y : B) : scala.Tuple2[A, B] = { /* compiled code */ }
def →[B](y : B) : scala.Tuple2[A, B] = { /* compiled code */ }
}
You can see that by the time B is evaluated, A is already "fixed". Let's just say that you can only (implicitly) convert the right hand side of a tuple when using -> operator:
implicit def stringToInt(str: String): Int = 10
implicit def intToStr(str: Int): String = "a"
val a: Map[Int, Int] = Map(10 -> "asd") //fine
val b: Map[Int, Int] = Map("asd" -> 20) // error! cant change left side
val c: Map[String, String] = Map("asd" -> 20) // fine
val d: Map[String, String] = Map(10 -> "asd") // error! cant change left side
Because of similar compiler quirks related to using operator ->, #Jorg's solution works in one direction, but not the other:
implicit def tupleIntifier(t: (String, Int)) = (10, 10)
implicit def tupleIntifier2(t: (Int, String)) = (10, 10)
val a: Map[Int, Int] = Map("asd" -> 20) // uses tupleIntifier
val b: Map[Int, Int] = Map(10 -> "asd") // fails!!
However, if you avoid using -> operator altogether and simply use (key, value) syntax, it will work:
val a: Map[Int, Int] = Map((10, "asd"))
val b: Map[Int, Int] = Map(("asd", 20))
implicit def stringToInt(str: String): Int = 15
println(a) // prints Map(10 -> 15)
println(b) // prints Map(15 -> 20)
Please, look at the error message you are getting:
error: type mismatch;
found : (String, Int)
required: (Int, Int)
val mm: Map[Int, Int] = Map("asd" -> 20)
^
The error message is not about String instead of Int, it is about (String, Int) instead of (Int, Int). So, you are simply converting the wrong thing:
implicit def tupleIntifier[T](t: (String, T)) = (10, t._2)
val mm: Map[Int, Int] = Map("asd" -> 20)
//=> mm: Map[Int,Int] = Map(10 -> 20)
Voila! It works.
If you were to add such a general implicit conversion, you would lose the type-safety that Scala is enforcing, because any String would become an Int as needed, anywhere, without intervention from the programmer.
In reality, when you want to create that map from other data, you probably already know the data types of that other data. So if the keys are known to be integers, convert them to Int and use them like that. Otherwise, use strings.
Your example is highly artificial. Which concrete problem are you trying to solve?

Why doesn't Function2 have an andThen method?

Why does andThen only exist for single argument functions in Scala?
The following code works:
val double = (x: Int) => x * 2
val timesFour = double andThen double
But why is there no andThen method for multi argument functions?
val multiply = (x: Int, y: Int) => x * y
val multiplyAndDouble = multiply andThen double
<console>:10: error: value andThen is not a member of (Int, Int) => Int
Surely it is trivial to add this method. Is there a reason it been omitted from the standard library?
I have just noticed it is easy to work around with the following:
val multiplyAndDouble = multiply.tupled andThen double
val res = multiplyAndDouble(1, 3) // res = 6
I can't speak as to why Function2 doesn't supply and andThen, but Scalaz defines Functor instances for functions of various arities where map is equivalent to andThen, meaning you could write
val multiplyAndDouble = multiply map double
There is a similar question here:
Scala API 2.10.*: Function2.andThen what happened to?, but there is also no answer. In my opinion it is possible. Here is working example for Scala 2.11.1:
object TestFunction2 {
def main(args: Array[String]): Unit = {
val double = (x: Int) => x * 2
val timesFour = double andThen double
println(timesFour(2)) // prints 8
val multiply = (x: Int, y: Int) => x * y
val multiplyAndDouble = multiply andThen double
println(multiplyAndDouble(1, 3)) // prints 6
}
implicit def toFunc2(function2: Function2[Int, Int, Int]): Func2[Int, Int, Int] = {
new Func2[Int, Int, Int] {
def apply(v1: Int, v2: Int): Int = function2(v1, v2)
}
}
}
trait Func2[-T1, -T2, +R] extends Function2[T1, T2, R] {
def andThen[A](g: R => A): (T1, T2) => A = { (x, y) => g(apply(x, y)) }
}
Another way to write theons's answer is to use:
val multiplyAndDouble = double compose multiply.tupled
val result = multiplyAndDouble(2, 6) // res 24

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.

"update" method with variable-length index in 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.