[F <: List[A], A] 和 [F[_] <: List[A], A] 之间的推理差异

Difference in inference between [F <: List[A], A] and [F[_] <: List[A], 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[_] <: List[A], A](as: F[A]): F[A]

scala> f(List(42))
val res1: List[Any] = List(42)

为什么类型参数 A 到类型构造函数 F 在第二种情况下被推断为 Any

根据我对你在第二种情况下的定义的解释,F[_] 是一个 List 类型的构造函数,但是 List[A] 必须是任何列表的上限 F[_] 可以构造,所以 A 必须是 Any.

可能你想要的是这个:

def f[F[_] <: List[_], A](as: F[A]) = as

def f[F[x] <: List[x], A](as: F[A]) = as

特别是对于x需要固定为多个约束参数的情况(示例请参考下面的@user评论)

在第一种情况下 F 是一个具体类型,所以 List[A] 不是所有列表的上限,而是仅在列表 F 上,所以 A 不必是 Any 并且最窄的可推断类型是 Int.

不是一个完整的答案,只是一些值得深思的问题:我试图构建一个反例,但在假设 A 将被推断为最窄的类型。不过,也许您会觉得它很有趣。

这里是一个具有类似约束的函数 h,但我们采用稍微不同的类型构造函数而不是 List

主要思想是Cc有两个独立的类型参数:

  • 首先是F[_]_的意思
  • 第二个是在<: Lst[A]-constraint
  • 中与A交互的那个

请注意,如果 A 被推断为最窄类型 (Nothing),则不会编译:

(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[_] <: 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)

如果 A 被推断为满足 <: Lst[A] 约束的最窄可能类型,那么 A 将是 Nothing,并且参数必须是输入T[Nothing] = Cc[Nothing, Nothing],无人居住

我认为这很有趣,但我不明白为什么如果不编译它实际上会很糟糕。