Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 519
St.Antario Avatar asked Feb 05 '23 17:02

St.Antario


2 Answers

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)
like image 159
slouc Avatar answered Feb 20 '23 21:02

slouc


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.

like image 44
Jörg W Mittag Avatar answered Feb 20 '23 20:02

Jörg W Mittag