Разница в выводе между [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], который необитаем.

Я думаю, это интересно, но я не понимаю, почему было бы плохо, если бы он не компилировался.

Другие вопросы по тегам