List("a").contains(5)
Поскольку
Int
никогда не может содержаться в спискеString
, этот должен генерировать ошибку во время компиляции , но это не так.Он расточительно и незаметно проверяет каждую
строку
, содержащуюся в списке, на равенство5
, что никогда не может быть истинным («5»
никогда не равно] 5
в Scala).Это было названо « проблема« содержит »». И некоторые подразумевали , что если система типов не может правильно ввести такую семантику, тогда зачем прилагать дополнительные усилия для обеспечения типов. Поэтому я считаю, что это важная проблема, которую необходимо решить.
Параметризация типа
B>: A
изList.contains
вводит любой тип, который является супертипом типаA
(тип содержащихся элементов в списке).trait List[+A] { def contains[B >: A](x: B): Boolean }
Параметризация этого типа необходима, потому что
+ A
объявляет, что список ковариантен для типаA
, таким образом,A
может не может использоваться в контравариантной позиции, т.е. как тип входного параметра. Ковариантные списки (которые должны быть неизменными) гораздо более эффективны для расширения , чем инвариантные списки (которые могут быть изменяемыми).
- это
String
в проблемном примере выше, ноInt
не является супертипомString
, так что же произошло? Неявное подчинение в Scala решило, чтоAny
является взаимным супертипом какString
, так иInt
.Создатель Scala, Мартин Одерски, предложил , что исправление будет заключаться в ограничении типа ввода
B
только теми типами, у которых есть метод равенства, которыйAny
не имеет.trait List[+A] { def contains[B >: A : Eq](x: B): Boolean }
Но это не решает проблему, потому что два типа (где тип ввода не является супертипом типа элементов списка) могут иметь общий супертип, который является подтипом
Любой
, то есть также подтипEq
. Таким образом, он будет компилироваться без ошибок, и неправильно типизированная семантика останется.Отключение неявного подчинения везде, где не является идеальным решением , потому что неявное подчинение - вот почему следующий пример для подчинения
Any
работает. И мы не хотели бы, чтобы нас заставляли использовать приведение типов, когда принимающий сайт (например, передавая в качестве аргумента функции) имеет правильно типизированную семантику для общего супертипа (который может даже не бытьAny
).trait List[+A] { def ::[B >: A](x: B): List[B] } val x : List[Any] = List("a", 5) // see[1]
[1] List.apply вызывает :: operator .
Итак, мой вопрос: как лучше всего решить эту проблему?
Мое предварительное заключение состоит в том, что неявное подчинение должно быть отключено на сайте определения, где семантика в противном случае типизирована неправильно.Я дам ответ, который покажет, как отключить неявное подчинение на сайте определения метода. Есть ли альтернативные решения?
Обратите внимание, что это общая проблема, которая не ограничивается списками.
ОБНОВЛЕНИЕ : Я подал запрос на улучшение и начал обсуждение scala по этому . Я также добавил комментарии под ответами Ким Стебель и Петера Шмитца, показывающие, что их ответы имеют ошибочную функциональность. Таким образом, решения нет. Также в вышеупомянутой ветке обсуждения я объяснил, почему я считаю, что ответ soc неправильный.