Почему передается Int, где ожидается параметр F [_]?
Скажем, мы имеем функцию:
def bar[F[_], A](x: F[A], y: F[A]) = null
Все следующие действия ясны:
bar(List(1, 2, 3), List(1)) // compile ok
bar(List(1), Some(1)) // doesn't compile
Но,
bar(1, List(1)) // compile ok
bar(1, 1) // compile ok
Почему?
P.S. пример из FSiS Part 1 - Конструкторы типов, функторы и вид проектора
Ответы
Ответ 1
Я думаю, что следующее дает ключ (хотя есть еще какая-то тайна):
def baz[F[_], A](x: F[A]): F[A] = x
scala> baz("str")
res5: Comparable[String] = str
scala> baz(1)
res6: Any = 1
scala> class Foo
defined class Foo
scala> baz(new Foo)
<console>:15: error: no type parameters for method baz: (x: F[A])F[A] exist so that it can be applied to arguments (Foo)
--- because ---
argument expression type is not compatible with formal parameter type;
found : Foo
required: ?F
baz(new Foo)
^
<console>:15: error: type mismatch;
found : Foo
required: F[A]
baz(new Foo)
^
scala> case class Foo2
warning: there were 1 deprecation warning(s); re-run with -deprecation for details
defined class Foo2
scala> baz(Foo2)
res10: scala.runtime.AbstractFunction0[Foo2] = Foo2
Таким образом, функции bar
и baz
находят любой тип контейнера, который они могут (Comparable
для String, AbstractFunction0
для класса case и т.д.), чтобы соответствовать ожидаемому F[_]
.
Спекуляция: в случае Int
, я подозреваю, что мы нажимаем специальный тип "контейнер" для того, что (в штучной) примитивные типы в базовом байт-коде. Если этот специальный тип можно напечатать только до Scala как "Any
", но на самом деле это какой-то особый тип, который мы можем назвать "Any[_]
", то это может объяснить результаты, которые мы видим. Как подтверждение того, что это связано с особой обработкой примитивов, обратите внимание, что это не выполняется для примитивных простых типов, таких как (небезопасный) класс Foo
выше.
Ответ 2
Я думаю, что вы сталкиваетесь с ограничением системы вывода типов. Чтобы пролить свет на это, давайте посмотрим, что произойдет, когда мы переопределим это немного, чтобы получить более полезный вывод:
class Bar[F[_], A](x: F[A], y: F[A]) {}
res0: Bar[List,Int] = [email protected]
new Bar(List(1,2,3), List(1))
res1: Bar[Any,Int] = [email protected]
new Bar(List(1), 1)
res2: Bar[Any,Int] = [email protected]
new Bar(List(1), Some(1))
<console>:12: error: inferred kinds of the type arguments (Product with java.io.Serializable,Int) do not conform to the expected kinds of the type parameters (type F,type A) in class Bar.
Product with java.io.Serializable type parameters do not match type F expected parameters:
<refinement of Product with java.io.Serializable> has no type parameters, but type F has one
new Bar(List(1), Some(1))
^
<console>:12: error: type mismatch;
found : List[Int]
required: F[A]
new Bar(List(1), Some(1))
^
<console>:12: error: type mismatch;
found : Some[Int]
required: F[A]
new Bar(List(1), Some(1))
В первом примере мы имеем a Bar[List, Int]
, что имеет смысл, мы прошли через два List[Int]
.
Во втором и третьем мы имеем a Bar[Any, Int]
. Здесь, где это становится странным. Имейте в виду, что Any
является родительским элементом как AnyVal
(родительский элемент Scala эквивалентов примитивов Java) и AnyRef
(эквивалент Scala объекта Java) (см. Scala Документация для дальнейшего объяснения).
Scala вывод типа решил, что этот конструктор Bar
должен принять Any
для F
и Int
для A
. Поскольку Any
действительно является родителем List
и Int
, это прекрасно. List
действительно параметризуется как [Int]
, так что штраф. Что странно в том, что Scala в порядке, говоря, что Int
также имеет тип Any[Int]
. У меня нет хорошего объяснения этой части.
С последним, где я честно смущен, и мне нужно задаться вопросом, является ли это ошибкой. По какой-то причине, хотя оба List
и Some
являются дочерними элементами Any
, и оба параметрируются с помощью Int
, это не позволяет. Боюсь, я не очень разбираюсь в тонкостях методов вывода компилятора, но для того, что стоит, явным образом указываю эти параметры:
new Bar[Any,Int](List(1), Some(1))
res14: Bar[Any,Int] = [email protected]
Для меня это предполагает, что система вывода типов просто не может правильно вывести типы или вывести неверные типы.
Ответ 3
Кажется, что что-то происходит с типом Any
в сочетании с системой вида.
Any
не принимает параметр типа:
val i:Any[Int] = 1
дает ошибку, поэтому она должна быть простой.
Any
может использоваться в позиции, где ожидается более высокий тип типа, как показано в примере bar[Any, Nothing](1,1)
Если мы используем Any
в более высокосортной позиции, этот параметр типа волшебным образом превращается в простой вид, а параметр типа этого прежнего более высокого типа типа полностью игнорируется.
Если параметр первого типа bar
равен Any
, мы можем, но любой тип, как второй параметр, и он всегда будет компилироваться:
bar[Any,String](List(1),List(2))
bar[Any, Boolean](1,2)
bar[Any, Int](List(), true)
case class A()
bar[Any, A](List, A)
Кажется, что проблема с выводом типа приводит к тому, что некоторые примеры терпят неудачу без аннотаций типов.
Я узнал об этом поведении с помощью проб и ошибок, я не знаю, была ли эта ошибка или функция;-)
Ответ 4
Это работает, потому что компилятор передает вам некоторые типы. Вот как это выглядит с типами, добавленными в bar
:
bar[Any, Int](1, List(1)) // compile ok
bar[Any, Nothing](1, 1) // compile ok
Это не работает для bar(List(1), Some(1))
, потому что компилятор не может вывести тип, поддерживающий как List
, так и Some
. Вы можете, однако, опустить это, как показано выше, bar[Any, Int](List(1), Some(1))
работает.