List("a").contains(5)
Поскольку Int никогда не может содержаться в списке String, этот должен генерировать ошибку во время компиляции, но это не так.
Он расточительно и молча проверяет каждый String, содержащийся в списке, для равенства 5, который никогда не может быть правдой ("5" никогда не равен 5 в Scala).
Это было названо " проблема" содержит "". И некоторые подразумевали, что если система типов не может правильно напечатать такую семантику, то зачем вам прибегать к дополнительным усилиям по обеспечению соблюдения типов. Поэтому я считаю, что это важная проблема.
Параметризация типа B >: A List.contains вводит любой тип, который является супертипом типа A (тип элементов, содержащихся в списке).
trait List[+A] {
def contains[B >: A](x: B): Boolean
}
Эта параметризация необходима, потому что +A объявляет, что список ковариант для типа A, поэтому A не может использоваться в контравариантная позиция, т.е. тип входного параметра. Ковариантные списки (которые должны быть неизменными) гораздо более мощные для расширения, чем инвариантные списки (которые могут быть изменчивыми).
A является String в проблемном примере выше, но Int не является супертипом String, так что случилось? неявное предположение в Scala, решил, что Any является взаимным супертипом как String, так и Int.
Создатель Scala, Мартин Одерски, предложил, что исправление будет заключаться в том, чтобы ограничить тип ввода B только теми типы, которые имеют метод equals, который Any не имеет.
trait List[+A] {
def contains[B >: A : Eq](x: B): Boolean
}
Но это не решает проблему, потому что два типа (где тип ввода не является супертипом типа элементов списка) могут иметь взаимный супертип, который является подтипом Any, то есть также подтип 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 вызывает оператор::.
Итак, мой вопрос в том, что является лучшим решением этой проблемы?
Мой предварительный вывод состоит в том, что неявное предположение должно быть отключено на сайте определения, где семантика в противном случае неверно набрана. Я дам ответ, который показывает, как отключить неявное подчинение на сайте определения метода. Существуют ли альтернативные решения?
Обратите внимание, что эта проблема является общей и не изолирована от списков.
UPDATE: отправил запрос на улучшение и начал scala обсуждение темы. Я также добавил комментарии в ответах Ким Стебеля и Питера Шмитца, в которых показано, что их ответы имеют ошибочную функциональность. Таким образом, решения нет. Кроме того, в вышеупомянутой теме обсуждения я объяснил, почему я считаю, что ответ сотовой связи неверен.