Search code examples
scalaimplicit-conversionimplicit

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?


Solution

  • 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)