2016-11-11 12 views
4

Я попытался неявные преобразования в следующем примере:Применяя неявное преобразование к карте

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 

Почему мы не можем применить неявные преобразования для сопоставления ключей? Есть ли способ обойти это?

ответ

5

Это не работает, потому что вы используете ->, который является (встроенный) оператор:

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 */ } 
} 

Вы можете видеть, что к тому времени, B вычисляется, А уже «фиксированный». Давайте просто скажем, что вы можете только (неявно) преобразовать правую часть кортежа при использовании -> оператора:

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 

Из-за подобные причуды компилятора, связанных с использованием оператора ->, @ решение Йорга работает в одном направлении, но не другой:

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!! 

Однако, если вы не использовать -> оператора в целом и просто использовать (key, value) синтаксис, он будет работать:

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

Если вы хотите добавить такое общее неявное преобразование, вы потеряете безопасность типа, которую применяет Scala, поскольку любая String станет Int по мере необходимости, где угодно, без вмешательства программиста. В действительности, когда вы хотите создать эту карту из других данных, вы, вероятно, уже знаете типы данных этих других данных. Поэтому, если ключи известны как целые числа, преобразуйте их в Int и используйте их так. В противном случае используйте строки. Ваш пример очень искусственный. Какую конкретную проблему вы пытаетесь решить?

+0

Реальная проблема довольно скучная, вот о написании тестов с помощью скалята. Мне нужно преобразование для удобочитаемости. –

2

Пожалуйста, обратите внимание на сообщение об ошибке вы получаете:

error: type mismatch; 
found : (String, Int) 
required: (Int, Int) 
     val mm: Map[Int, Int] = Map("asd" -> 20) 
             ^

сообщение об ошибке не о String вместо Int, речь идет о (String, Int) вместо (Int, Int). Итак, вы просто конвертируете неправильную вещь:

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! Оно работает.

+0

Прохладный, спасибо большое. Но почему первые примеры работают? –