Implicit conversion and map key - scala

I've defined an enum
object SupportedCurrencies extends Enumeration {
type SupportedCurrencies = Value
val USD, GBP, ARS, AUD, BRL, CAD, CHF, CNY, EUR, JPY, SEK, DKK, NOK = Value
}
and implicit conversion for it to string
implicit def supportedCurrencyToString(currency: SupportedCurrencies.Value): String = currency.toString
but if I'll try to create a Map[String, Int]
val m: Map[String, Int] = Map(USD -> 1)
I'm getting an error
type mismatch;
[error] found : (helpers.SupportedCurrencies.Value, Int)
[error] required: (String, Int)
[error] val m: Map[String, Int] = Map(USD -> 1)
Could anyone explain what's wrong here?

I looked closely at the error and get it
implicit def supportedCurrencyToString(currencyEntry: (helpers.SupportedCurrencies.Value, Int)): (String, Int) = currencyEntry._1.toString -> currencyEntry._2
I was needed conversion from pair to pair

Related

scala 2.13 auto implicit resolution error

I am facing this weird problem related to scala implicit resolution
Here is the code snippet
import scala.collection.Factory
import scala.collection.immutable.Seq
sealed trait A
sealed trait B
case class BImpl() extends B
case class AImpl() extends A
object implicitsContainer {
type AB = (A, B)
implicit def toStringAnyTuples[C[X] <: Iterable[X], A <: AB]
(col: C[A])
(implicit factory: Factory[(String, Any), C[(String, Any)]])
: C[(String, Any)] = {
factory.fromSpecific(col.iterator.map(f => f._1.toString -> f._2))
}
}
object Main extends App {
import implicitsContainer._
def a(f: Seq[(String, Any)]): Seq[(String, Any)] = f
val w: Seq[(AImpl, BImpl)] = Seq(AImpl() -> BImpl())
val x: Seq[(String, Any)] = a(w)
// Won't compile
// val y: Seq[(String, Any)] = a(Seq(AImpl() -> BImpl()))
}
Scala automatically picking up the implicit method
implicit def toStringAnyTuples[C[X] <: Iterable[X], A <: AB](col: C[A])
(implicit factory: Factory[(String, Any), C[(String, Any)]])
: C[(String, Any)] = {
factory.fromSpecific(col.iterator.map(f => f._1.toString -> f._2))
}
for this: -
val w: Seq[(AImpl, BImpl)] = Seq(AImpl() -> BImpl())
val x: Seq[(String, Any)] = a(w)
but throws an error for this
val y: Seq[(String, Any)] = a(Seq(AImpl() -> BImpl()))
and the error is:-
Error:(44, 47) type mismatch;
found : (AImpl, BImpl)
required: (String, Any)
val y: Seq[(String, Any)] = a(Seq(AImpl() -> BImpl()))
and one more point, if I remove the type from w
val w = Seq(AImpl() -> BImpl())
val x: Seq[(String, Any)] = a(w)
then also this will work fine.
The only error is with
val y: Seq[(String, Any)] = a(Seq(AImpl() -> BImpl()))
I am using: -
SCALA -> 2.13.3
SBT -> 1.3.13
JAVA -> 14
It's just type inference issue. Type parameter of Seq.apply was not inferred. Try
val y: Seq[(String, Any)] = a(Seq[(AImpl, BImpl)](AImpl() -> BImpl()))
or
val y: Seq[(String, Any)] = a(Seq[(A, B)](AImpl() -> BImpl()))
What you are experiencing is a symptom of the way inference works in the Scala compiler.
Here is a smaller example that shows the same issue:
object Test {
class C[T](x: T)
implicit def conv(c: C[Int]): C[String] = ???
def m(cs: C[String]) = 1
val ci = new C(1)
def t1: Int = m(ci) // OK
def t2: Int = m(new C(1)) // found: Int, expected: String
}
When type-checking new C(1), the compiler pushes down the expected type String to type
checking the expression 1, which fails. In the line above, type checking ci with expected type
C[String] succeeds thanks to the implicit conversion.
My suggestion here would be to define an extension method that performs the conversion, instead
of making the conversion implicit. This is also recommended for clarity - implicit conversions like
the one defined in your example can lead to surprising, hard to diagnose issues.
In my example, it would look like this:
object Test {
class C[T](x: T)
implicit class CExt(private val c: C[Int]) extends AnyVal {
def toCString: C[String] = ???
}
def m(cs: C[String]) = 1
val ci = new C(1)
def t1: Int = m(ci.toCString)
def t2: Int = m(new C(1).toCString)
}

implicit conversion for a list of tuple

I am trying to convert implicitly a List[(Int, String)] to List[(IntWrap, String)], which is giving an error of TypeMismatch.
I tried few other conversion which works which are
List[Int] to List[IntWrap] and Tuple2[Int, String] to Tuple2[IntWrap, String] which works.
case class IntWrap(a : Int)
implicit def int2IntWrap(x: Int) = IntWrap(x)
implicit def int2IntWrapForTuple2(tuple: Tuple2[Int, String]) = (IntWrap(tuple._1), tuple._2)
//Defined few methods
def foo(t : (IntWrap, String)) = println(t._2)
def foo_list(t: List[IntWrap]) = println(t.size)
def foo_list_tuple(t : List[(IntWrap, String)]) = println(t.size)
foo(3 -> "hello") //this works
foo_list(List(1, 2, 3)) //this works
val l : List[(IntWrap, String)] = List(3 -> "hello")
foo_list_tuple(l) //this works
val l1 = List(3 -> "hello")
foo_list_tuple(l1) //this one doesn't work
//error: type mismatch; found: List[(Int, String)] required: List[(IntWrap, String)]
How can I make this work?
Try defining implicit conversion like so
implicit def listOfTupleToListOfIntWrapTuple(l: List[(Int, String)]): List[(IntWrap, String)] =
l.map(tuple => (IntWrap(tuple._1), tuple._2))

Tuple with Int member not recognized where Long is required

I've got a function that expects a Tuple2, where one item must be a long. When I attempt to pass pairs with literal numbers using the -> implicit, they are being interpreted as Int instead of Long.
Here, the problem is demonstrated:
scala> val x: (Long, String) = (5, "test")
x: (Long, String) = (5,test)
scala> val x: (Long, String) = 5L -> "test"
x: (Long, String) = (5,test)
scala> val x: (Long, String) = 5 -> "test"
<console>:43: error: type mismatch;
found : (Int, String)
required: (Long, String)
val x: (Long, String) = 5 -> "test"
I suppose the problem is that when the implicit is applied, type inference locks in on Int, but in the first example, type inference is nice enough to consider the number a Long.
But my DSL works much better if I can use -> and omit the L suffix. Is there a way to achieve this?
Unfortunately, Int is not formally a subtype of Long. Thus, an (Int, String) can not be passed where a (Long, String) is expected, despite the fact that Tuple2 is covariant in its type parameters.
However, Int is viewable as Long. So, you can design a function that will upconvert an Int to a Long the following way:
scala> def goodFunc[LongLike <% Long](arg: (LongLike, String)) = { "whatevs" }
goodFunc: [LongLike](arg: (LongLike, String))(implicit evidence$1: LongLike => Long)String
scala> goodFunc(5 -> "test")
res4: String = whatevs
Or, better yet, use:
def goodFunc[LongLike](arg: (LongLike, String))(implicit ev: LongLike => Long) = { "whatevs" }
, since <% is deprecated.

How to find instance of a value type in Scala?

I know isInstanceOf can be used to find value type, but how do I find the type of 'str'?
What type is it?
scala> val str = ("Scala", "Elixir","Spark")
str: (String, String, String) = (Scala, Elixir, Spark)
The following throws an error (exclude Any/AnyRef etc for now):
scala> str.isInstanceOf[List]
<console>:13: error: type List takes type parameters
str.isInstanceOf[List]
scala> str.isInstanceOf[String]
<console>:13: warning: fruitless type test: a value of type (String, String, String) cannot also be a String (the underlying of String)
str.isInstanceOf[String]
^
res9: Boolean = false
I can check it this way but is there a name for this?
scala> str.isInstanceOf[(String, String, String)]
res12: Boolean = true
Use :type in scala repl to find type
Actual type is Tuple3[String, String, String]
str.isInstanceOf[Tuple3[String, String, String]]
Scala REPL
scala> val str = ("Scala", "Elixir","Spark")
str: (String, String, String) = (Scala,Elixir,Spark)
scala> :type str
(String, String, String)
scala> str.isInstanceOf[Tuple3[String, String, String]]
res2: Boolean = true
scala> str.getClass
res3: Class[_ <: (String, String, String)] = class scala.Tuple3
How to determine val type programmatically?
For instance, you want to know the type of the value is some specific type?
// assign x value
val x: Any = "this is string"
// custom function evaluate type
def f[T](v: T) = v match {
case _: Int => println("Int")
case _: String => println("String")
case _ => println("Unknown")
}
// determine val type
f(x)
Yet another way to determine a type programmatically is using Manifest:
scala> def getType[T: Manifest](t: T): Manifest[T] = manifest[T]
getType: [T](t: T)(implicit evidence$1: Manifest[T])Manifest[T]
scala> val str = ("Scala", "Elixir","Spark")
str: (String, String, String) = (Scala,Elixir,Spark)
scala> getType(str)
res0: Manifest[(String, String, String)] = scala.Tuple3[java.lang.String, java.lang.String, java.lang.String]
The modern (meaning Scala 2.10 or later), Scala way of programmatically getting the compile time type of something is using a TypeTag.
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> def getType[T: TypeTag](value: T) = typeOf[T]
getType: [T](value: T)(implicit evidence$1: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.Type
scala> val str = ("Scala", "Elixir","Spark")
str: (String, String, String) = (Scala,Elixir,Spark)
scala> println(getType(str))
(java.lang.String, java.lang.String, java.lang.String)
scala> getType(str) <:< typeOf[(String,String,String)]
res1: Boolean = true
scala> getType((1,2,3)) <:< typeOf[(String,String,String)]
res2: Boolean = false
getClass will give your the erased runtime class. isInstanceOf[T] will test whether the erased runtime class is the same as or a subclass of the erased runtime class of T.
And "erased" means that the following evaluates to true.
(1,2,3).isInstanceOf[(String,String,String)]
"runtime" and "compile time" mean that this is true:
val a: Any = (1,2,3)
a.isInstanceOf[(_,_,_)]
while this is false:
val a: Any = (1,2,3)
getType(a) <:< typeOf[(Int,Int,Int)]
This method can help you get the type of any val/var at runtime, it's also works in compiled code.
import scala.reflect.runtime.universe._
def printType[T](x: T)(implicit tag: TypeTag[T]): Unit = println(tag.tpe.toString)
printType(List[Int](1,2,3)) // output: List[Int]
printType(("xxx", 123, 0.1)) // output: (String, Int, Double)
printType(2) // output: Int

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?