Подтипирование приводит к Any: ошибка в компиляторе или проблема с моим кодом?

Позвольте мне сразу перейти к проблеме, с которой я столкнулся, пока возился с ограничениями типов.
Давайте рассмотрим следующее... Я создал функцию 'foo', подобную этой

def foo[A,B](x:A,y:B):(A,B)=(x,y)

Я вызвал foo на листе scala, например

foo("Mars",2400)

Я получил результат, как

res0: (String, Int) = (Mars,2400)

Обратите внимание на выведенные типы Mars и 2400
Теперь я хотел обеспечить, чтобы функция 'foo' принимала целые числа, числа с плавающей запятой или числа Double (любой тип, являющийся подтипом AnyVal).

Для обеспечения соблюдения я написал код вроде

def fooNew[A<:B,B](x:A,y:B):(A,B)=(x,y)

Типы, выведенные из предыдущего кода, были (String, Int), и когда я вызывал fooNew, например

fooNew("Saturn",2400)

Я был удивлен, увидев, что компилятор пропустил мой код и не вызвал ошибку, вместо этого он выдал результат, подобный

res0: (String, Any) = (Saturn,2400)

Желаемый способ принуждения здесь не сработал. Если бы я сделал что-то вроде этого

def fooNew[A<:B,B<:AnyVal](x:A,y:B):(A,B)=(x,y)

Компилятор наверняка выдал бы мне ошибку, и это произошло!

Error:(2, 2) inferred type arguments [String,Any] do not conform to method fooNew's type parameter bounds [A <: B,B <: AnyVal]
fooNew("Saturn",2400);}

Я хочу спросить, почему компилятор не выбрал тип Int, а вывел тип Any и позволил моему коду пройти проверку типов?
Всегда ли мне нужно принудительно принуждать второй тип быть подтипом AnyVal вместо того, чтобы позволять компилятору делать вывод за меня? или это ошибка в компиляторе.
Прошу прощения, если мой вопрос вводит в заблуждение или не соответствует вашим ожиданиям.
В настоящее время я использую scala-library 2.11.8
Спасибо.


person Aniruddha Sinha    schedule 31.08.2016    source источник


Ответы (2)


Вы можете думать об использовании исходного объявления с выведенными типами как «найти A и B так, чтобы x имел тип A, y имел тип B, а A был подтипом B». Поскольку A = String и B = Any удовлетворяют этим условиям, компилятор правильно их выводит (есть и другие решения, например A = B = Any, но это наиболее конкретное).

Но вы можете изменить объявление, чтобы сказать компилятору: «Найти A и B так, чтобы x имел тип A, а y — тип B, а затем проверить, что A является подтипом B». Это делается следующим образом:

def fooNew[A,B](x:A,y:B)(implicit evidence: A <:< B): (A,B)=(x,y)

Это работает, потому что компилятор будет использовать только первый список параметров для вывода A и B. Найдите «ограничения обобщенного типа», чтобы найти дополнительную информацию о <:< и =:=.

person Alexey Romanov    schedule 31.08.2016
comment
Это замечательно. Просто расширил свои знания о выводе типов и ограничениях типов. - person Samar; 31.08.2016
comment
это именно то, что я хотел... идеальное решение - person Aniruddha Sinha; 31.08.2016

def fooNew[A<:B,B](x:A,y:B):(A,B)=(x,y)

В приведенном выше примере вы объявляете параметр типа A подтипом параметра типа B. Когда вы передаете A как String и B как Int, компилятор идет вверх по иерархии классов, чтобы найти подходящий тип для B, так что Int является a B, а также String является подтипом B. Единственный тип в иерархии, удовлетворяющий этим двум условиям, — это тип Any. Итак, String является подтипом Any, а Int имеет тип Any.

person Samar    schedule 31.08.2016
comment
но не должно ли это вызывать ошибку, потому что мой мотив заключается в том, чтобы использовать типы для обоих аргументов. - person Aniruddha Sinha; 31.08.2016
comment
Я не думаю, что со стороны компилятора вполне допустимо повышать выведенный тип до некоторого родительского типа, когда тот же выводит желаемый тип, когда подтипирование не выполняется. - person Aniruddha Sinha; 31.08.2016
comment
Чтобы применить определенный тип, вы можете указать конкретный тип в параметре типа. Если вы не предоставляете конкретный тип, компилятор может искать другой тип, который удовлетворяет вашему определению метода. - person Samar; 31.08.2016