类型 Lambda - 更多细节
语法
Type ::= ... | TypeParamClause ‘=>>’ Type
TypeParamClause ::= ‘[’ TypeParam {‘,’ TypeParam} ‘]’
TypeParam ::= {Annotation} (id [HkTypeParamClause] | ‘_’) TypeBounds
TypeBounds ::= [‘>:’ Type] [‘<:’ Type]
类型检查
像 [X] =>> F[X]
这样的类型 Lambda 定义了一个从类型到类型的函数。参数可以带有边界。如果参数有边界,例如 [X >: L <: U] =>> F[X]
,则会检查参数传递给参数是否符合边界 L
和 U
。只有上界 U
可以是 F 绑定的,即 X
可以出现在其中。
子类型规则
假设两个类型 Lambda
type TL1 = [X >: L1 <: U1] =>> R1
type TL2 = [X >: L2 <: U2] =>> R2
那么 TL1 <: TL2
,如果
- 类型区间
L2..U2
包含在类型区间L1..U1
中(即L1 <: L2
且U2 <: U1
), R1 <: R2
这里我们依赖于 alpha 重命名 来匹配两个绑定类型 X
。
部分应用的类型构造函数,例如 List
,被认为等同于其 eta 展开。即,List = [X] =>> List[X]
。这允许类型构造函数与类型 lambda 进行比较。
与参数化类型定义的关系
参数化类型定义
type T[X] = R
被视为一个简写,对应于一个非参数化定义,其右侧为类型 lambda
type T = [X] =>> R
如果类型定义带有 +
或 -
方差注解,则会检查类型 lambda 是否满足方差注解。例如,
type F2[A, +B] = A => B
展开为
type F2 = [A, B] =>> A => B
同时检查参数 B
在 A => B
中是否协变出现。
参数化抽象类型
type T[X] >: L <: U
被视为一个简写,对应于一个非参数化抽象类型,其边界为类型 lambda。
type T >: ([X] =>> L) <: ([X] =>> U)
但是,如果 L
为 Nothing
,则它不会被参数化,因为 Nothing
被视为所有种类的底类型。例如,
type T[X] <: X => X
展开为
type T >: Nothing <: ([X] =>> X => X)
而不是
type T >: ([X] =>> Nothing) <: ([X] =>> X => X)
相同的展开也适用于类型参数。例如,
[F[X] <: Coll[X]]
被视为一个简写,对应于
[F >: Nothing <: [X] =>> Coll[X]]
抽象类型和不透明类型别名会记住它们创建时的方差。因此,类型
type F2[-A, +B]
已知在 A
中逆变,在 B
中协变,并且只能用满足这些约束的类型实例化。同样地
opaque type O[X] = List[X]
O
已知是不变的(而不是协变,因为其右侧会暗示)。另一方面,透明别名
type O2[X] = List[X]
将被视为协变,X
在其右侧协变使用。
注意: 将 Nothing
视为通用底类型的决定是暂时的,可能会在进一步讨论后更改。
注意: Scala 2 和 3 的区别在于 Scala 2 还将 Any
视为通用顶类型。Scala 3 中没有这样做。另请参阅有关 类型多态 的讨论。
柯里化类型参数
类型 lambda 的主体可以再次是类型 lambda。示例
type TL = [X] =>> [Y] =>> (X, Y)
目前,没有为推断此类柯里化类型 lambda 的类型参数做出特殊规定。这留待将来工作。