Разница в выводе между [F <: Список [A], A] и [F [_] <: Список [A], A]
Рассмотрим разницу в выводе аргумента типа для конструкторов типов в следующих двух предложениях параметра типа.
$ scala3-repl
scala> def f[F <: List[A], A](as: F) = as
def f[F <: List[A], A](as: F): F
scala> f(List(42))
val res0: List[Int] = List(42)
scala> def f[F[_] <: List[A], A](as: F[A]) = as
def f[F[_$1] <: List[A], A](as: F[A]): F[A]
scala> f(List(42))
val res1: List[Any] = List(42)
Почему аргумент типа
A
набрать конструктор
F
выводится как
Any
во втором случае?
2 ответа
Основываясь на моей интерпретации вашего определения во втором случае, это
List
конструктор типа, но должен быть верхней границей в любом списке
F[_]
можно построить, так и должно быть.
Вероятно, вы к этому стремились:
def f[F[_] <: List[_], A](as: F[A]) = as
Или же
def f[F[x] <: List[x], A](as: F[A]) = as
особенно для случаев, когда
x
необходимо зафиксировать для нескольких параметров ограничения (пример см. в комментариях @user ниже)
В первом случае это конкретный тип, поэтому
List[A]
не верхняя граница во всем списке, а только в списке
F
, так
A
не должно быть
Any
и самый узкий выводимый тип будет
Int
.
Не полноценный ответ, просто пища для размышлений: я попытался построить контрпример, но не смог придумать ничего, что на самом деле привело бы к несостоятельности в предположении, что это будет считаться самым узким типом . Тем не менее, возможно, вам это покажется интересным.
Вот функция
h
с аналогичными ограничениями, но вместо
List
, возьмем конструкторы немного другого типа.
Основная идея заключается в том, что
Cc
имеет два отдельных параметра типа:
- Первое - это то, что имеется в виду под
_
вF[_]
- Второй - тот, который взаимодействует с -ограничением
Обратите внимание, что это не будет компилироваться, если предполагается, что это самый узкий тип ():
(run in 3.0.0-RC2)
scala> trait Lst[+X]
// defined trait Lst
scala> case class Cc[+I, +X](i: I) extends Lst[X]
// defined case class Cc
scala> type T[+I] = Cc[I, Nothing]
// defined alias type T[+I] = Cc[I, Nothing]
scala> def h[F[_] <: Lst[A], A](as: F[A]) = as
def h[F[_$1] <: Lst[A], A](as: F[A]): F[A]
scala> val xs: T[Int] = Cc(42)
val xs: T[Int] = Cc(42)
scala> h(xs)
val res9: Cc[Int, Nothing] = Cc(42)
Был выведен как самый узкий тип, удовлетворяющий ограничению
<: Lst[A]
, тогда
A
было бы
Nothing
, и аргумент должен быть типа
T[Nothing] = Cc[Nothing, Nothing]
, который необитаем.
Я думаю, это интересно, но я не понимаю, почему было бы плохо, если бы он не компилировался.