Странное поведение при конкатенации последовательности к карте (Scala 2.13) - PullRequest
0 голосов
/ 07 апреля 2020

У меня есть эта простая программа ниже. Я ожидал, что все результаты укажут, что карта, соединенная с последовательностью, даст карту. Странно, что в случае 3 вместо карты отображается итерация. Я предполагаю, что это связано с тем, что на карте есть два разных оператора "++", как показано ниже. Поэтому вопрос в том, в чем разница между «Map [_, _]» и «Map [Any, Any]». Спасибо за ваши идеи.

enter image description here

    def test1[A, B](map: Map[A, B], a: A, B: B) = {
      val map2 = map ++ Seq(a -> b)
      println("Test 1 " + map.getClass + " / " + map2.getClass)
    }

    def test2(map: Map[Any, Any], a: Any, B: Any) = {
      val map2 = map ++ Seq(a -> b)
      println("Test 2 " + map.getClass + " / " + map2.getClass)
    }

    def test3(map: Map[_, _], a: Any, B: Any) = {
      val map2 = map ++ Seq(a -> b)
      println("Test 3 " + map.getClass + " / " + map2.getClass)
    }

    // Test 1 class scala.collection.immutable.Map$Map1 / class scala.collection.immutable.Map$Map2
    test1(Map(1 -> "A"), 2, "B")

    // Test 2 class scala.collection.immutable.Map$Map1 / class scala.collection.immutable.Map$Map2
    test2(Map(1 -> "A"), 2, "B")

    // Test 3 class scala.collection.immutable.Map$Map1 / class scala.collection.immutable.$colon$colon
    test3(Map(1 -> "A"), 2, "B")

1 Ответ

0 голосов
/ 07 апреля 2020

В первых двух случаях у вас возникает ситуация, когда у вас есть Map[K, V] и Seq[(K, V)] - потому что Seq[A] <: Iterable[A], Scala выберет более конкретный c случай с ++(seq: Iterable[(K, V)]): Map[K V].

В третьем случае, однако, вы используете экзистенциальный тип. Это означает, что «существует некоторый тип X и Y, для которого ваш тип равен Map[X, Y]», тогда как добавляемая вами последовательность имеет вид Seq[(A, B)] - A, а B может иметь тип X и Y, но так как мы ничего о них не знаем, мы не можем этого допустить. Вы можете передать пустую карту типа Map[Nothing, Nothing] или Map[Unit, Unit] = Map(() -> ()), и она с радостью примет ее.

Что она может сделать в этом случае? Ну, все Map являются последовательностями пар (что гарантирует, что первый элемент пары является уникальным), и он обеспечивает ++, который именно это и делает - объединяет ваш Iterable[Z] с этой последовательностью.

Итак, все сводится к тому, что _ в сигнатуре типа означает экзистенциальный тип. Map[_, _] является ярлыком для Map[X, Y] forSome { type X; type Y }. Вы знаете, что есть какой-то тип, который удовлетворяет требованиям, но ни вы, ни компилятор не знают достаточно, чтобы что-то доказать об этом. И поскольку Scala (и большинство типизированных языков) выбирает наиболее конкретное c определение (что означает "более конкретный c", определено в спецификации языков), Map[X, Y] ++ Seq[(X, Y)] выбирает версию, которая возвращает Map[X, Y], и когда он не может доказать, что типы выровнены, он возвращает Iterable.

Если вы запустите это в аммоните для проверки ваших типов вывода, вы увидите, что тип третьего случая равен Iterable[(Any, Any)], поскольку Scala имел найти общий тип для (_, _) и (K, V). REPL лучше отображает типы interred, чем отражение времени выполнения, которое теряет информацию из-за стирания типов.

@     def test1[A, B](map: Map[A, B], a: A, b: B) = {
        map ++ Seq(a -> b)
      }
defined function test1

@

@     def test2(map: Map[Any, Any], a: Any, b: Any) = {
        map ++ Seq(a -> b)
      }
defined function test2

@

@     def test3(map: Map[_, _], a: Any, b: Any) = {
        map ++ Seq(a -> b)
      }
defined function test3

@

@     test1(Map(1 -> "A"), 2, "B")
res15: Map[Int, String] = Map(1 -> "A", 2 -> "B")

@

@     test2(Map(1 -> "A"), 2, "B")
res16: Map[Any, Any] = Map(1 -> "A", 2 -> "B")

@

@     test3(Map(1 -> "A"), 2, "B")
res17: collection.immutable.Iterable[(Any, Any)] = List((1, "A"), (2, "B"))
...