问题描述
考虑以下两个类型参数子句中类型参数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
?
解决方法
根据我对您在第二种情况下的定义的解释,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
。
不是一个完整的答案,只是一些思考:我试图构建一个反例,但在假设 {{1} } 将被推断为最窄的类型。不过,也许你会觉得它很有趣。
这是一个具有类似约束的函数 A
,但我们采用了略有不同的类型构造函数来代替 h
。
主要思想是 List
有两个独立的类型参数:
- 第一个是
Cc
中的_
- 第二个是在
F[_]
约束中与A
交互的那个
请注意,如果 <: Lst[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[_$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
,并且参数必须是类型 { {1}},无人居住。
我认为这很有趣,但我不明白为什么如果不编译它实际上会很糟糕。