Scala Set, что происходит с инвариантными типами?

Выполняя рефакторинг в приложении scala, я столкнулся с ситуацией, когда при переходе от List к Set возник вопрос, которого у меня раньше не было. У меня есть некоторое представление о дисперсии, но я хотел бы понять, что именно она означает для компилятора.

У меня было что-то подобное, которое компилируется и отлично работает:

case class MyClassList(s: List[Any])
val myList = List(("this", false)) // List[(String, Boolean)]
val listWorks = MyClassList(myList)

Затем я изменил свой список, чтобы установить:

case class MyClassSet(s: Set[Any])
val mySet = Set(("this", false)) // Set[(String, Boolean)]
val setFails = MyClassSet(mySet)

На этом этапе создание объекта типа MyClassSet больше не подходит, когда я передаю Set в качестве аргумента, даже если он принимает Set of Any. Теперь это немного сбивает с толку, когда работает следующее (обратите внимание, что набор «такой же», как и предыдущий mySet):

val setWorks1 = MyClassSet(Set(("this", false)))

Я считаю, что простое объяснение состоит в том, что компилятор выводит mySet val как Set [(String, Boolean)], но когда я создаю его непосредственно в списке аргументов setWorks1, поскольку он принимает Set [Any], компилятор выводя его как Set [Any]. Это делает первый пример неудачным, а второй - успешным. Эти также работают, что указывает на правильность предыдущего:

val setWorks2 = MyClassSet(mySet.toSet[Any])
val mySetOfAny: Set[Any] = Set(("this", false), ("that", true), ("other", false))
val setWorks3 = MyClassSet(mySetOfAny)

Фактическая ошибка, показанная компилятором:

Error:(15, 55) type mismatch;
found   : Set[(String, Boolean)]
required: Set[Any]
Note: (String, Boolean) <: Any, but trait Set is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: (...)

Списки и наборы определены следующим образом:

type List[+A]  = scala.collection.immutable.List[A]
type Set[A]    = immutable.Set[A]
  • Является ли эта разница в вариации типов, которая позволяет мне передавать список «более ограниченного типа, чем любой» в качестве аргумента, но не a в случае Set?
  • Разве эта разница предотвращает только приведение типов или преобразование между ними?
  • Это в основном «ограничение» компилятора или ожидаемое свойство инвариантного типа?
  • Есть ли какие-то другие различия между инвариантными типами «на практике» или они сводятся к приведению типов, как это?

person negative    schedule 13.10.2016    source источник


Ответы (1)


1) Здесь объясняется: Почему неизменяемый набор Scala не ковариантны по типу?

По сути, Set [T] тоже Function1[T, Boolean]. Подпись Function1 - [-In, +Out], поэтому T не может быть одновременно +T и -T, поскольку scala не допускает двувариантности (это значительно ослабит систему типов).

2) Вы можете легко использовать его с помощью .toSet[Any] (который является оболочкой над asInstanceOf). Также есть способ пропустить проверку отклонений.

3, 4) Это ожидаемое свойство универсальных (полиморфных) типов. Они могут быть инвариантными / ковариантными / контравариантными (не только) и формально описываются простыми правилами. Вы можете прочитать объяснение здесь: https://stackoverflow.com/a/27627891/1809978

person dk14    schedule 13.10.2016
comment
Принимаю ваш ответ, так как после прочтения всех остальных ответов в связанных ответах я понял это. Фактическая разница в 1) хорошо объясняет, почему Set инвариантен (также потому, что он реализован с использованием Maps, у которых есть инвариантный ключ. Спасибо - person negative; 13.10.2016