Выборочно отключить подсемейство в Scala? (правильно введите List.contains)

List("a").contains(5)

Потому чтоInt никогда не может содержаться в спискеString, этодолжен генерировать ошибку во время компиляции, но это не так.

Тщательно и бесшумно проверяет каждыйString содержится в списке на равенство5, который никогда не может быть правдой ("5" никогда не равняется5 в Скале).

Это было названопроблема «содержит»". А такженекоторые намекают что если система типов не может правильно набирать такую семантику, то зачем делать дополнительные усилия для принудительного применения типов. Поэтому я считаю, что это важная проблема, которую нужно решить.

Тип параметризацииB >: A изList.contains вводит любой тип, который является супертипом типаA (тип элементов, содержащихся в списке).

trait List[+A] {
   def contains[B >: A](x: B): Boolean
}

Этот тип параметризации необходим, потому что+A заявляет, что списокковариант по типуAтаким образомA не может использоваться в контравариантной позиции, т. е. в качестве типа входного параметра. Ковариантные списки (которые должны быть неизменными)гораздо более мощный для расширения чем инвариантные списки (которые могут быть изменяемыми).

A этоString в проблемном примере выше, ноInt не супертипString, так что случилось?неявное подчинение в Скале, решил, чтоAny это взаимный супертип обоихString а такжеInt.

Создатель Scala Мартин Одерски,предложенный что исправление будет ограничивать тип вводаB только те типы, которые имеют метод равных, что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 вызывает оператор ::.

Итак, мой вопрос, что является лучшим решением этой проблемы?

Мой предварительный вывод заключается в том, что неявное подчинение должно быть отключено на месте определения, где в противном случае семантика неправильно введена. Я предоставлю ответ, который покажет, как отключить неявное подчинение на сайте определения метода. Есть ли альтернативные решения?

Обратите внимание, что эта проблема носит общий характер и не изолирована от списков.

ОБНОВИТЬ: У меня естьподал запрос на улучшение и началобсуждение этой темы, Я также добавил комментарии к ответам Ким Стебель и Питера Шмитца, показывающие, что их ответы имеют ошибочную функциональность. Таким образом, нет решения. Также в вышеупомянутой ветке обсуждения я объяснил, почему я думаю, что ответ сока неверен.