2

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类型构造函数的类型参数?FAny

4

2 回答 2

3

根据我对您在第二种情况下的定义的解释,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]不是所有列表的上限,而只是在 list 上F,所以A不必是Any,最窄的可推断类型是Int

于 2021-04-11T19:52:45.793 回答
1

不是一个完整的答案,只是一些值得深思的东西:我试图构建一个反例,但在A被推断为最窄类型的假设下,我无法提出任何实际上会导致不合理的东西. 不过,也许你觉得它很有趣。

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

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

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

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[_$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)

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

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

于 2021-04-11T23:33:34.930 回答