类型
上面的语法描述了用户代码中可以编写的类型的具体语法。Scala 类型系统中对类型的语义操作最好用内部类型来定义,内部类型是从具体类型语法中反糖化的。
内部类型
以下抽象语法定义了内部类型的形状。在本规范中,除非另有说明,"类型" 指的是内部类型。内部类型抽象掉了诸如优先级和分组之类的无关细节,并包含无法使用具体语法直接表达的类型形状。它们还包含对复杂具体语法类型的简化、分解形状,例如细化类型。
具体类型到内部类型的转换
具体类型被递归地转换为内部类型,或反糖化。大多数具体类型的形状与内部类型的形状一一对应。我们将在下文中详细说明其他类型的转换。
中缀类型
具体中缀类型 ´T_1´ op
´T_2´ 由一个中缀运算符 op
组成,该运算符应用于两个类型操作数 ´T_1´ 和 ´T_2´。该类型被转换为内部类型应用 op
´[T_1, T_2]´。中缀运算符 op
可以是任意标识符。
类型运算符遵循与项运算符相同的优先级和结合性。例如,A + B * C
解析为 A + (B * C)
,而 A | B & C
解析为 A | (B & C)
。以冒号 ‘:’ 结尾的类型运算符是右结合的;所有其他运算符都是左结合的。
在一系列连续的类型中缀运算 ´t_0 \, \mathit{op} \, t_1 \, \mathit{op_2} \, ... \, \mathit{op_n} \, t_n´ 中,所有运算符 ´\mathit{op}_1, ..., \mathit{op_n}´ 必须具有相同的结合性。如果它们都是左结合的,则该序列被解释为 ´(... (t_0 \mathit{op_1} t_1) \mathit{op_2} ...) \mathit{op_n} t_n´,否则它被解释为 ´t_0 \mathit{op_1} (t_1 \mathit{op_2} ( ... \mathit{op_n} t_n) ...)´。
在 -source:future
下,如果类型名称是字母数字,并且目标类型没有标记为 infix
,则会发出弃用警告。
类型运算符 |
和 &
并不是真正的特殊。然而,除非被遮蔽,它们会解析为 基本类型别名 scala.|
和 scala.&
,它们分别代表 并集类型和交集类型。
函数类型
具体函数类型 ´(T_1, ..., T_n) \Rightarrow R´ 代表一组函数值,这些函数值接受类型为 ´T_1, ..., Tn´ 的参数,并产生类型为 ´R´ 的结果。只有一个参数类型 ´T \Rightarrow R´ 的情况是 ´(T) \Rightarrow R´ 的简写。形式为 ´\Rightarrow T´ 的参数类型代表类型为 ´T´ 的 按名称传递参数。
函数类型向右结合,例如 ´S \Rightarrow T \Rightarrow R´ 等同于 ´S \Rightarrow (T \Rightarrow R)´。
函数类型转换为定义了 apply
方法的内部类类型。具体来说,´n´ 元函数类型 ´(T_1, ..., T_n) \Rightarrow R´ 转换为内部类类型 scala.Function´_n´[´T_1´, ..., ´T_n´, ´R´]
。特别是 ´() \Rightarrow R´ 是类类型 scala.Function´_0´[´R´]
的简写。
此类类类型表现得好像它们是以下特性的实例
它们的精确超类型和实现可以在本文档的标准库页面中的 函数类部分 中查询。
依赖函数类型 是参数已命名并且可以在结果类型中引用的函数类型。在具体类型 ´(x_1: T_1, ..., x_n: T_n) \Rightarrow R´ 中,´R´ 可以引用参数 ´x_i´,特别是形成路径依赖类型。它转换为内部 精炼类型
其中 ´S´ 是 ´R´ 的最小超类型,不提及任何 ´x_i´。
多态函数类型 是接受类型参数的函数类型。它们的结果类型必须是函数类型。在具体类型 ´[a_1 >: L_1 <: H_1, ..., a_n >: L_1 <: H_1] => (T_1, ..., T_m) => R´ 中,类型 ´T_j´ 和 ´R´ 可以引用类型参数 ´a_i´。它转换为内部精炼类型
元组类型
元组类型 ´(T_1, ..., T_n)´,其中 ´n \geq 2´,是类型 ´T_1´ *: ... *: ´T_n´ *: scala.EmptyTuple
的语法糖,它本身是一系列嵌套的 infix 类型,这些类型是 *:[´T_1´, *:[´T_2´, ... *:[´T_n´, scala.EmptyTuple]]]
的语法糖。´T_i´ 可以是通配符类型参数。
注意
(´T_1´)
是类型 ´T_1´,而不是´T_1´ *: scala.EmptyTuple
(在这种情况下,´T_1´ 不能是通配符类型参数)。()
不是有效的类型(即它不会被反糖化为scala.EmptyTuple
)。
具体细化类型
在类型的具体语法中,细化可以包含多个细化定义。它们都必须是抽象的。此外,细化定义可以相互引用,也可以引用父类型的成员,即它们可以访问 this
。
在内部类型中,每个细化定义恰好定义一个细化定义,并且对 this
的引用必须在递归类型中显式地进行。
从具体语法到抽象语法的转换如下
- 创建一个新的递归 this 名称 ´\alpha´。
- 将细化定义中对
this
的所有隐式或显式引用替换为 ´\alpha´。 - 为每个细化定义创建嵌套的 细化类型。
- 除非 ´\alpha´ 实际上从未被使用过,否则将结果包装在 递归类型
{ ´\alpha´ => ´...´ }
中。
具体类型 lambda
在具体类型 lambda 参数的顶层,不允许使用方差注释。但是,在内部类型中,所有类型 lambda 参数都有显式的方差注释。
将具体类型 lambda 转换为内部类型 lambda 时,每个类型参数的方差是根据其在类型 lambda 主体中的用法推断出来的。
定义
从这里开始,我们默认情况下引用内部类型。
种类
Scala 类型系统本质上是高阶的。类型要么是适当类型,要么是类型构造函数,要么是多阶类型。
- 适当类型是项的类型。
- 类型构造函数是类型到类型的类型级函数。
- 多阶类型可以采用各种种类。
所有类型都相对于一个 一致性 关系 ´<:´ 存在于单个格中。顶层类型 是 AnyKind
,底层类型 是 Nothing
:所有类型都符合 AnyKind
,而 Nothing
符合所有类型。它们可以分别用 基本类型别名 scala.AnyKind
和 scala.Nothing
来引用。
类型可以是具体的或抽象的。抽象类型 ´T´ 始终具有下界和上界 ´L´ 和 ´H´,使得 ´L >: T´ 和 ´T <: H´。具体类型 ´T´ 被认为具有自身作为下界和上界。
类型的种类由其(传递)上界指示
- 类型
´T <:´ scala.Any
是一个适当类型。 - 类型
´T <: K´
,其中 ´K´ 是一个 类型 lambda(形式为[´\pm a_1 >: L_1 <: H_1´, ..., ´\pm a_n >: L_n <: H_n´] =>> ´U´
)是一个类型构造函数。 - 其他类型是多态的;它们既不是适当类型也不是类型构造函数。
因此,AnyKind
本身是多态的。Nothing
是通用种类的:它同时具有所有种类,因为它符合所有类型。
使用这种表示,很少需要显式地谈论类型的种类。通常,类型的种类是通过其边界隐式地确定的。
另一种看待它的方式是类型边界就是种类。它们表示类型的集合:´>: L <: H´ 表示满足 ´L <: T´ 和 ´T <: H´ 的类型 ´T´ 的集合。类型的集合可以被视为类型的类型,即种类。
约定
类型边界在形式上始终是 ´>: L <: H´
的形式。按照惯例,我们可以省略写入中的任一或两个边界。
- 省略时,下界 ´L´ 为
Nothing
。 - 省略时,上界 ´H´ 为
Any
(不是AnyKind
)。
这些约定对应于具体语法中的默认值。
适当类型
适当类型也称为值类型,因为它们表示值的集合。
稳定类型 是包含恰好一个非 null
值的值类型。稳定类型可以用作命名 指示符类型 中的前缀。稳定类型是
- 引用稳定项的指示符类型,
- this 类型,
- 超类型,
- 字面量类型,
- 递归 this 类型,以及
- 斯科伦类型。
每个稳定类型 ´T´ 都是具体的,并且具有一个底层类型 ´U´,使得 ´T <: U´。
类型构造函数
每个类型构造函数都对应一个推断类型参数子句,该子句按如下方式计算
类型定义
一个类型定义 ´D´ 代表一个 type
成员定义的右侧或类型参数的边界。它可以是
- 一个形式为 ´= U´ 的类型别名,或
- 一个带有边界 ´>: L <: H´ 的抽象类型定义。
所有类型定义都有一个下界 ´L´ 和一个上界 ´H´,它们都是类型。对于类型别名,´L = H = U´。
类型参数的类型定义永远不是类型别名。
类型
类型 Lambda
一个形式为 [´\pm a_1 >: L_1 <: H_1´, ..., ´\pm a_n >: L_n <: H_n´] =>> ´U´
的类型 lambda 是一个带有 ´n´ 个类型参数的类型构造函数的直接表示。当应用于符合指定边界的 ´n´ 个类型参数时,它会生成另一个类型 ´U´。类型 lambda 始终是具体类型。
类型参数的作用域扩展到结果类型 ´U´ 以及类型参数本身的边界。
所有类型构造函数都符合某个类型 lambda。
类型 lambda 参数的类型边界处于逆变位置,而其结果类型处于协变位置。如果某个类型构造函数 ´T <:´ [´\pm a_1 >: L_1 <: H_1´, ..., ´\pm a_n >: L_n <: H_n´] =>> ´U´
,那么 ´T´ 的第 ´i´ 个类型参数边界包含边界 ´>: L_i <: H_i´,并且其结果类型符合 ´U´。
注意:类型 lambda 的具体语法不允许为类型参数指定方差。相反,方差是从 lambda 的主体推断出来的,尽可能通用。
示例
指示符类型
指示符类型(或简称指示符)是对定义的引用。术语指示符引用术语定义,而类型指示符引用类型定义。
在抽象语法中,id
保留了它是术语还是类型。在具体语法中,id
引用类型指示符,而 id.type
引用术语指示符。在这种情况下,术语指示符通常被称为单例类型。
具有空前缀 ´\epsilon´ 的指示符称为直接指示符。它们引用作用域中可用的本地定义
- 本地
type
、object
、val
、lazy val
、var
或def
定义 - 项或类型参数
直接指示符的 id
在抽象语法中受到保护,防止意外遮蔽。它们保留了所引用确切定义的标识,而不是依赖于基于作用域的名称解析。 1
´\epsilon´ 前缀不能在具体语法中写出。而是使用裸 id
,并根据作用域解析。
命名指示符引用非空前缀的成员定义
- 顶级定义,包括顶级类,具有包引用前缀
- 类成员定义和细化具有类型前缀
项指示符
引用项定义 t
的项指示符 ´p.x´ 具有底层类型 ´U´。如果 ´p = \epsilon´ 或 ´p´ 是包引用,则底层类型 ´U´ 是 t
的声明类型,并且 ´p.x´ 是稳定类型,当且仅当 t
是 val
或 object
定义。否则,底层类型 ´U´ 和 ´p.x´ 是否为稳定类型由 memberType
(´p´, ´x´)
确定。
所有项指示符都是具体类型。如果 scala.Null ´<: U´
,则项指示符表示由 null
和 ´t´ 表示的值组成的集合,即 t eq v
的值 ´v´。否则,指示符表示仅包含 ´v´ 的单例集合。
类型指示符
引用类定义(包括特质和隐藏对象类)的类型指示符 ´p.C´ 是类类型。如果类是单态的,则类型指示符是值类型,表示 ´C´ 或其任何子类的实例集。否则,它是一个类型构造函数,具有与类定义相同的类型参数。所有类类型都是具体、非稳定类型。
如果类型指示符 ´p.T´ 不是类类型,它指的是类型定义 T
(类型参数或 type
成员定义),并且具有一个底层 类型定义。如果 ´p = \epsilon´ 或 ´p´ 是包引用,则底层类型定义是 T
的声明类型定义。否则,它由 memberType
(´p´, ´T´)
确定。非类类型指示符是具体的(分别为稳定的),当且仅当其底层类型定义是别名 ´U´ 且 ´U´ 本身是具体的(分别为稳定的)。
参数化类型
参数化类型 ´T[T_1, ..., T_n]´ 由类型构造函数 ´T´ 和类型参数 ´T_1, ..., T_n´ 组成,其中 ´n \geq 1´。参数化类型是良构的,如果
- ´T´ 是一个接受 ´n´ 个类型参数 ´a_1, ..., a_n´ 的类型构造函数,即它必须符合形式为 ´[\pm a_1 >: L_1 <: H_1, ..., \pm a_n >: L_n <: H_n] => U´ 的类型 lambda,并且
- 如果 ´T´ 是一个抽象类型构造函数,则没有一个类型参数是通配符类型参数,并且
- 每个类型参数都符合其边界,即,给定 ´\sigma´ 替换 ´[a_1 := T_1, ..., a_n := T_n]´,对于每个类型 ´i´
- 如果 ´T_i´ 是一个类型,并且 ´\sigma L_i <: T_i <: \sigma H_i´,或者
- ´T_i´ 是一个通配符类型参数 ´? >: L_{Ti} <: H_{Ti}´ 并且 ´\sigma L_i <: L_{Ti}´ 并且 ´H_{Ti} <: \sigma H_i´。
´T[T_1, ..., T_n]´ 是一个参数化类类型,当且仅当 ´T´ 是一个 类类型。所有参数化类类型都是值类型。
在通配符类型参数的具体语法中,如果省略了两个边界,则实际边界从目标类型构造函数(必须是具体的)中对应类型参数的边界推断。如果只省略了一个边界,则像往常一样使用 Nothing
或 Any
。
同样在具体语法中,出于兼容性原因,可以使用 _
代替 ?
,含义相同。这种替代方案将在将来被弃用,并且在 -source:future
下已经过时。
简化规则
在协变或逆变位置使用的通配符类型参数始终可以简化为普通类型。
令 ´T[T_1, ..., T_n]´ 为具体类型构造函数的参数化类型。然后,在 ´i´ 位置应用通配符类型参数 ´? >: L <: H´ 遵循以下等价关系
- 如果类型参数 ´T_i´ 被声明为协变,则 ´T[..., ? >: L <: H, ...] =:= T[..., H, ...]´。
- 如果类型参数 ´T_i´ 被声明为逆变,则 ´T[..., ? >: L <: H, ...] =:= T[..., L, ...]´。
示例参数化类型
给定部分类型定义
以下参数化类型是格式良好的
以下类型是格式错误的
以下代码也包含格式错误的类型
此类型
此类型 ´C´.this
表示类 ´C´ 中 ´C´ 的 this
值。
此类型通常隐式地作为 指定类型 的前缀出现,这些指定类型引用 ´C´ 的成员。它们在类型系统中扮演着特殊的角色,因为它们会受到类型上的 从...看 操作的影响。
此类型是稳定类型。´C´.this
的底层类型是 ´C´ 的 自身类型。
超类型
超类型 ´C´.super[´D´]
表示类 C
中 C
的 this
值,但“扩展”到仅看到来自父类或特征 ´D´ 的成员。
超类型存在是为了与 Scala 2 兼容,Scala 2 允许对内部类进行遮蔽。在仅限 Scala 3 的上下文中,超类型始终可以用相应的 此类型 替换。因此,我们在本规范中省略了对超类型的进一步讨论。
字面量类型
字面量类型 lit
表示单个字面量值 lit
。因此,类型断言 1: 1
为字面量值 1
提供最精确的类型:字面量类型 1
。
在运行时,表达式 e
被认为具有字面类型 lit
,如果 e == lit
。具体来说,e.isInstanceOf[lit]
和 e match { case _ : lit => }
的结果由评估 e == lit
决定。
字面类型可用于所有原始类型,以及 String
。但是,只有 Int
、Long
、Float
、Double
、Boolean
、Char
和 String
的字面类型可以在具体语法中表达。
字面类型是稳定类型。它们的底层类型是包含其值的原始类型。
示例
按名类型
按名类型 ´=> T´ 表示按名参数的声明类型。按名类型只能作为方法类型中参数的类型出现,以及作为 参数化类型 中的类型参数。
带注释的类型
带注释的类型 ´T a´ 将 注释 ´a´ 附加到类型 ´T´ 上。
示例
以下类型将 @suspendable
注释添加到类型 String
细化类型
细化类型 ´T { R }´ 表示属于 ´T´ 并且也具有符合细化 ´R´ 的成员的值集。
如果细化类型 ´T { R }´ 是良构的,则
- ´T´ 是一个适当的类型,并且
- 如果 ´R´ 是一个项(
def
或val
)细化,则细化类型是一个适当的类型,并且 - 如果 ´R´ 覆盖了 ´T´ 的成员,则适用于 覆盖 的通常规则,并且
- 如果 ´R´ 是一个具有 多态方法类型 的
def
细化,则 ´R´ 覆盖了 ´T´ 的成员定义。
作为最后一条规则的例外,如果 ´T <:´ scala.PolyFunction
并且 ´id´ 是名称 apply
,则允许多态方法类型细化。
如果细化 ´R´ 不覆盖 ´T´ 的任何成员,并且不是 scala.PolyFunction
例外的出现,则该细化被称为“结构化” 2。
注意:由于细化不定义类,因此无法使用this 类型来引用父类型 ´T´ 中的术语和类型成员。当细化类型的表面语法进行此类引用时,递归类型将包装细化类型,通过递归 this 类型提供对自身成员的访问。
示例
给定以下类定义
我们得到以下一致性关系
U <: T { def foo: Int }
U <: T { def fooPoly[A](x: A): A }
U <: (T { def foo: Int }) { def fooPoly[A](x: A): A }
(我们可以将细化类型链接起来以细化多个成员)V <: T { type X <: Some[Any] }
V <: T { type X >: Some[Nothing] }
V <: T { type X = Some[Int] }
V <: T { def bar: Any }
(结构细化)
以下细化类型格式不正确
T { def barPoly[A](x: A): A }
(多态方法类型的结构细化)T { type X <: List[Any] }
(不满足覆盖规则)List { def head: Int }
(父类型List
不是适当类型)T { def foo: List }
(细化类型List
不是适当类型)T { def foo: T.this.X }
(T.this
不允许在T
的主体之外使用)
递归类型
形式为 { ´\alpha´ => ´T´ }
的递归类型表示与 ´T´ 相同的值,同时为 ´T´ 提供对其递归 this 类型 ´\alpha´
的访问。
递归类型不能直接在具体语法中表达。当具体语法中的细化类型包含需要访问 this
值的细化时,它们会根据需要创建。每个递归类型都定义一个唯一的自引用 ´\alpha´
,与系统中的任何其他递归类型不同。
递归类型可以在子类型化期间根据需要展开,将对 ´\alpha´
的引用替换为对一致性关系另一侧的稳定引用。
示例
给定细化类型部分中的类定义,我们可以使用以下细化类型在源语法中编写
此类型不能直接作为单独的细化类型表达,因为细化无法访问 this
值。相反,在类型的抽象语法中,它被转换为 { ´\alpha´ => ´T´ { def foo: ´\alpha´.X } }
。
给定以下定义
我们可以检查 z ´<:´ { ´\alpha´ => ´T´ { def foo: ´\alpha´.X } }
。我们首先展开递归类型,将 ´z´ 替换为 ´\alpha´,得到 z ´<:´ T { def foo: z.X }
。由于 ´z´ 的底层类型是 ´Z´,我们可以将 z.X
解析为 Option[Int]
,然后验证 z ´<:´ T
以及 z
是否具有成员 def foo: Option[Int]
。
联合类型和交集类型
从语法上讲,类型 S | T
和 S & T
是中缀类型,其中中缀运算符分别是 |
和 &
(参见 中缀类型)。
但是,在本规范中,´S | T´ 和 ´S & T´ 分别指代联合类型和交集类型的底层核心概念。
- 类型 ´S | T´ 表示由 ´S´ 或 ´T´ 表示的值集。
- 类型 ´S & T´ 表示由 ´S´ 和 ´T´ 同时表示的值集。
从 一致性规则 中关于联合类型和交集类型的规则,我们可以证明 ´&´ 和 ´|´ 是可交换的和可结合的。此外,&
对 |
是可分配的。对于任何类型 ´A´、´B´ 和 ´C´,以下所有关系都成立
- ´A & B =:= B & A´,
- ´A | B =:= B | A´,
- ´(A & B) & C =:= A & (B & C)´,
- ´(A | B) | C =:= A | (B | C)´,以及
- ´A & (B | C) =:= (A & B) | (A & C)´。
如果 ´C´ 是一个协变或逆变类型构造器,´C[A] & C[B]´ 可以使用以下规则简化
- 如果 ´C´ 是协变的,´C[A] & C[B] =:= C[A & B]´
- 如果 ´C´ 是逆变的,´C[A] & C[B] =:= C[A | B]´
上述两个规则的右到左有效性可以从协变和逆变的定义以及联合类型和交集类型的一致性规则推导出来
- 当 ´C´ 是协变时,我们可以推导出 ´C[A & B] <: C[A] & C[B]´。
- 当 ´C´ 是逆变时,我们可以推导出 ´C[A | B] <: C[A] & C[B]´。
联合类型的合并
在某些情况下,可能需要将联合类型扩展为非联合类型。为此,我们将联合类型 ´T_1 | ... | T_n´ 的合并定义为 ´T_1, ..., T_n´ 的基类实例的最小交集类型。请注意,联合类型仍然可能作为类型参数出现在结果类型中,这保证了合并始终是有限的。
例如,给定
´A | B´ 的连接是 ´C[A | B] & D´
Skolem 类型
Skolem 类型不能直接在具体语法中写出。此外,虽然它们是正确的类型,但它们永远不会被推断为是项定义(val
、var
和 def
)类型的组成部分。它们只在子类型推导过程中临时使用。
Skolem 类型是稳定类型。形式为 ´∃ \alpha : T´ 的 Skolem 类型表示对类型为 ´T´ 的未知值的稳定引用。标识符 ´\alpha´ 在每次创建 Skolem 类型时都会被唯一地选择。但是,由于 Skolem 类型是稳定的,因此它可以在其他类型中的多个出现位置被替换。当通过替换“复制”时,所有副本都保留相同的 ´\alpha´,因此是等价的。
方法类型
方法类型不是真正的类型。它们不是类型格的一部分。
但是,它们与类型共享一些元属性。特别是,当包含在进行某些替换的其他类型中时,替换会传递到方法类型中的类型。因此,将它们视为类型本身通常很方便。
方法类型用作至少有一个项或类型参数列表的 def
定义的“声明类型”。
方法类型
方法类型在内部表示为 ´(\mathit{Ps})U´,其中 ´(\mathit{Ps})´ 是参数名称和类型 ´(p_1:T_1, ..., p_n:T_n)´ 的序列,其中 ´n \geq 0´,而 ´U´ 是(值或方法)类型。此类型表示命名方法,这些方法接受名为 ´p_1, ..., p_n´ 的参数,类型为 ´T_1, ..., T_n´,并返回类型为 ´U´ 的结果。
方法类型向右关联:´(\mathit{Ps}_1)(\mathit{Ps}_2)U´ 被视为 ´(\mathit{Ps}_1)((\mathit{Ps}_2)U)´。
方法类型不存在作为值的类型。如果方法名称用作值,则其类型将 隐式转换为 对应的函数类型。
示例
定义
产生类型
多态方法类型
多态方法类型,简称 多态类型,在内部表示为 [´\mathit{tps}\,´]´T´
,其中 [´\mathit{tps}\,´]
是类型参数部分 [´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]
,其中 ´n \geq 0´,而 ´T´ 是(值或方法)类型。此类型表示命名方法,这些方法接受类型参数 ´S_1, ..., S_n´
,这些参数 符合 下界 ´L_1, ..., L_n´
和上界 ´U_1, ..., U_n´
,并产生类型为 ´T´ 的结果。
示例
定义
产生类型
类型操作
本节定义了关于类型和方法类型的几个元函数。
baseType(´T´, ´C´)
: 计算形式为´p´.´C´[´T_1, ..., T_n´]
的最小类型 ´U´,使得 ´T <: U´。asSeenFrom(´T´, ´C´, ´p´)
: 将在类 ´C´ 内部可见的类型 ´T´ 重新基准化,使其“从”前缀 ´p´ “看”起来。memberType(´T´, ´id´)
: 查找类型的成员 (T.id
) 并计算其底层类型或边界。
这些元函数是相互递归的。
基本类型
元函数 baseType(´T´, ´C´)
,其中 ´T´ 是一个适当的类型,´C´ 是一个类标识符,计算形式为 ´p.C´
或 ´p.C´[´U_1, ..., U_n´]
的最小类型 ´U´,使得 ´T <: U´。如果不存在这样的类型,则该函数未定义。baseType
的主要目的是在继承链中替换前缀和类类型参数。
我们如下定义 baseType(´T´, ´C´)
。为了简洁,我们将 ´p.X´[´U_1, ..., U_n´]
写成 ´p.X´
,其中 ´n = 0´。
baseType(´T = p.C´[´T_1, ..., T_n´], ´C´) ´≜ T´
baseType(´p.D´[´T_1, ..., T_n´], ´C´)
,其中´D´ ≠ ´C ≜ \sigma W´
,如果 ´Q´ 已定义,其中- ´D´ 声明为
´D[\pm a_1 >: L_1 <: H_1, ..., \pm a_n >: L_n <: H_n]´ extends ´P_1, ..., P_m´
´Q =´ meet(baseType(´P_i´, ´C´)
对于所有 ´i´,使得baseType(´P_i´, ´C´)
已定义)
´W = Q´
如果 ´p = \epsilon´ 或 ´p´ 是一个包引用;否则,´W =´ asSeenFrom(´Q´, ´D´, ´p´)
(在这种情况下,´p´ 是一个稳定类型,并且 ´D´ 必须在另一个类 ´B´ 中声明)´\sigma = [a_1 := T_1, ..., a_n := T_n]´
是将 ´D´ 的声明类型参数替换为实际类型参数的替换
- ´D´ 声明为
baseType(´T_1 & T_2´, ´C´) ´≜´ meet´(´baseType(´T_1´, ´C´), baseType(´T_2´, ´C´)´)´
baseType(´T_1 | T_2´, ´C´) ´≜´ join´(´baseType(´T_1´, ´C´), baseType(´T_2´, ´C´)´)´
baseType(´T´, ´C´) ´≜´ baseType(superType(´T´), ´C´)
如果superType(´T´)
已定义
以上定义使用了以下辅助函数。
superType(´T´)
计算 ´T´ 的“下一个上界”,如果存在
superType(´T´)
,其中 ´T´ 是一个稳定类型,是其底层类型superType(´p.X´)
,其中 ´p.X´ 是一个非类类型指示符,是其底层类型定义的上界superType(´([a_1 >: L_1 <: H_1, ..., a_n >: L_n <: H_n]´ =>> ´U)[T_1, ..., T_n]´)
是´[a_1 =: T_1, ..., a_n := T_n]U´
(即类型 lambda 重写体的 beta 归约)superType(´T[T_1, ..., T_n]´)
是superType(´T´)´[T_1, ..., T_n]´
如果superType(´T´)
已定义
请注意,superType
的情况不会相互重叠,也不会与除基于 superType
的情况以外的任何 baseType
情况重叠。因此,baseType
的情况也不会相互重叠。这使得 baseType
成为一个算法上的偏函数。
meet(´p.C[T_1, ..., T_n]´, ´q.C[U_1, ..., U_n]´)
计算两个(参数化)类类型的交集,它们属于同一个类,而 join
计算并集
- 如果
´p =:= q´
为假,则未定义 - 否则,对于
´i \in 1, ..., n´
,令´W_i´
为´T_i & U_i´
用于meet
(分别为´T_i | U_i´
用于join
),如果´C´
的第´i´
个类型参数是协变的´T_i | U_i´
用于meet
(分别为´T_i & U_i´
用于join
),如果´C´
的第´i´
个类型参数是逆变的´T_i´
如果´T_i =:= U_i´
并且´C´
的第´i´
个类型参数是不变的- 否则未定义
- 如果任何
´W_i´
未定义,则结果未定义 - 否则,结果为
´p.C[W_1, ..., W_n]´
我们将 meet(´T_1, ..., T_n´)
泛化为一个序列,如下所示
- 对于
´n = 0´
未定义 - 对于
´n = 1´
为´T_1´
- 如果
meet(´T_1, ..., T_{n-1}´)
已定义,则为meet(meet(´T_1, ..., T_{n-1}´), ´T_n´)
- 否则未定义
示例
给定以下定义
我们有以下 baseType
结果
baseType(List[Int], List) = List[Int]
baseType(List[Int], Iterable) = Iterable[Int]
baseType(List[A] & Iterable[B], Iterable) = meet(Iterable[A], Iterable[B]) = Iterable[A & B]
baseType(List[A] & Foo, Iterable) = Iterable[A]
(因为baseType(Foo, Iterable)
未定义)baseType(Int, Iterable)
未定义baseType(Map[Int, String], Iterable) = Iterable[(Int, String)]
baseType(Map[Int, String] & Map[String, String], Map)
未定义(因为K
是不变的)
来源
元函数 `asSeenFrom(´T´, ´C´, ´p´)`,其中 `T` 是在类 `C` 内部可见的类型或方法类型,`p` 是一个稳定类型,将类型 `T` 从前缀 `p` 的角度重新定义。本质上,它将 `T` 中的 this 类型和类类型参数替换为从外部可见的适当类型。由于 `T` 在 `C` 内部可见,因此它可以包含 `C` 本身及其所有封闭类的 this 类型和类类型参数。封闭类的 this 类型必须映射到 `p` 的适当子前缀,而类类型参数必须映射到适当的具体类型参数。
`asSeenFrom(´T´, ´C´, ´p´)` 只有在 `p` 对 `C` 有一个基类型时才有意义,即如果 `baseType(´p´, ´C´)` 被定义。
我们定义 `asSeenFrom(´T´, ´C´, ´p´)`,其中 `baseType(´p´, ´C´) = ´q.C[U_1, ..., U_n]´` 如下
- 如果 `T` 是对某个类 `D` 的第 `i` 个类类型参数的引用
- 如果 `baseType(´p´, ´D´) ´= r.D[W_1, ..., W_m]´` 被定义,则 `W_i`
- 否则,如果 `q = \epsilon` 或 `q` 是一个包引用,则 `T`
- 否则,`q` 是一个类型,`C` 必须在另一个类 `B` 中定义,并且 `baseType(´q´, ´B´)` 必须被定义,则 `asSeenFrom(´T´, ´B´, ´q´)`
- 否则,如果 `T` 是一个 this 类型 `´D´.this`
- 如果 `D` 是 `C` 的子类,并且 `baseType(´p´, ´D´)` 被定义,则 `p`(当 `D = C` 时总是如此)
- 否则,如果 `q = \epsilon` 或 `q` 是一个包引用,则 `T`
- 否则,`q` 是一个类型,`C` 必须在另一个类 `B` 中定义,并且 `baseType(´q´, ´B´)` 必须被定义,则 `asSeenFrom(´T´, ´B´, ´q´)`
- 否则,`T`,其中每个类型组件 `T_i` 都映射到 `asSeenFrom(´T_i´, ´C´, ´p´)`。
为了方便起见,我们将 `asSeenFrom` 泛化到类型定义 `D`。
- 如果 `D` 是一个别名 `= U`,则 `asSeenFrom(´D´, ´C´, ´p´) = asSeenFrom(´U´, ´C´, ´p´)`。
- 如果 `D` 是一个带有边界 `>: L <: H` 的抽象类型定义,则 `asSeenFrom(´D´, ´C´, ´p´) = ´>:´ asSeenFrom(´L´, ´C´, ´p´) ´<:´ asSeenFrom(´H´, ´C´, ´p´)`。
成员类型
元函数 `memberType(´T´, ´id´, ´p´)`,其中 `T` 是一个适当的类型,`id` 是一个术语或类型标识符,`p` 是一个稳定类型,它找到一个类型的成员(`T.id`)并计算其从前缀 `p` 的角度看到的底层类型(对于术语)或类型定义(对于类型)。对于术语,它还计算术语是否稳定。`memberType` 是计算命名设计器类型的底层类型或底层类型定义的基本操作。
`memberType` 的结果 `M` 是以下之一
- 未定义,
- 一个带有底层类型或方法类型 `U` 和稳定标志的术语结果,
- 一个带有类 `C` 的类结果,或
- 一个带有底层类型定义 `D` 的类型结果。
作为简写,我们定义 `memberType(´T´, ´id´)` 与 `memberType(´T´, ´id´, ´T´)` 相同,当 `T` 是一个稳定类型时。
我们定义 memberType(´T´, ´id´, ´p´)
如下
- 如果 ´T´ 是一个可能带参数的类类型,形式为 ´q.C[T_1, ..., T_n]´(其中 ´n \geq 0´)
- 令 ´m´ 为 ´C´ 的 类成员,其名称为 ´id´。
- 如果 ´m´ 未定义,则结果未定义。
- 如果 ´m´ 是一个类定义,则结果是一个类结果,其类为 ´m´。
- 如果 ´m´ 是类 ´D´ 中的项定义,其声明类型为 ´U´,则结果是一个项结果,其底层类型为
asSeenFrom
(´U´, ´D´, ´p´)
,并且稳定标志为真,当且仅当 ´m´ 是稳定的。 - 如果 ´m´ 是类 ´D´ 中的类型成员定义,则结果是一个类型结果,其底层类型定义为
asSeenFrom
(´U´, ´D´, ´p´)
,其中 ´U´ 定义如下- 如果 ´m´ 是一个不透明类型别名成员定义,其声明定义为 ´>: L <: H = V´,则
- ´U´ 为 ´= V´,如果
´p = D.´this
或我们正在计算 透明模式 中的memberType
, - 否则 ´U´ 为 ´>: L <: H´。
- ´U´ 为 ´= V´,如果
- 否则,´U´ 为 ´m´ 的声明类型定义。
- 如果 ´m´ 是一个不透明类型别名成员定义,其声明定义为 ´>: L <: H = V´,则
- 如果 ´T´ 是另一个单态类型指示符,形式为 ´q.X´
- 令 ´U´ 为
memberType(´q´, ´X´)
- 令 ´H´ 为 ´U´ 的上限
- 结果为
memberType(´H´, ´id´, ´p´)
- 令 ´U´ 为
- 如果 ´T´ 是另一个带参数的类型指示符,形式为 ´q.X[T_1, ..., T_n]´(其中 ´n \geq 0´)
- 令 ´U´ 为
memberType(´q´, ´X´)
- 令 ´H´ 为 ´U´ 的上限
- 结果为
memberType(´H[T_1, ..., T_n]´, ´id´, ´p´)
- 令 ´U´ 为
- 如果 ´T´ 是一个带参数的类型 lambda,形式为
´([\pm a_1 >: L_1 <: H_1, ..., \pm a_n >: L_n <: H_n]´ =>> ´U)[T_1, ..., T_n]´
- 结果为
memberType(´[a_1 := T_1, ..., a_n := T_n] U´, ´id´, ´p´)
,即我们对类型 redex 进行 β 归约。
- 结果为
- 如果 ´T´ 是一个细化类型,形式为
´T_1´ { ´R´ }
- 令 ´M_1´ 为
memberType(´T_1´, ´id´, ´p´)
的结果。 - 如果细化 ´R´ 的名称不是 ´id´,则令 ´M_2´ 未定义。
- 否则,令 ´M_2´ 为细化 ´R´ 的类型或类型定义,以及它是否稳定。
- 结果为
mergeMemberType(´M_1´, ´M_2´)
。
- 令 ´M_1´ 为
- 如果 ´T´ 是一个并集类型,形式为 ´T_1 | T_2´
- 令 ´J´ 为 ´T´ 的 并集。
- 结果为
memberType(´J´, ´id´, ´p´)
。
- 如果 ´T´ 是一个交集类型,形式为 ´T_1 & T_2´
- 令 ´M_1´ 为
memberType(´T_1´, ´id´, ´p´)
的结果。 - 令 ´M_2´ 为
memberType(´T_2´, ´id´, ´p´)
的结果。 - 结果为
mergeMemberType(´M_1´, ´M_2´)
。
- 令 ´M_1´ 为
- 如果 ´T´ 是一个递归类型,形式为
{ ´\alpha´ => ´T_1´ }
- 结果为
memberType(´T_1´, ´id´, ´p ´)
。
- 结果为
- 如果 ´T´ 是一个稳定类型
- 令 ´U´ 为 ´T´ 的基础类型。
- 结果为
memberType(´U´, ´id´, ´p´)
。
- 否则,结果未定义。
我们定义辅助函数 mergeMemberType(´M_1´, ´M_2´)
如下
- 如果 ´M_1´ 或 ´M_2´ 未定义,则结果为另一个。
- 否则,如果 ´M_1´ 或 ´M_2´ 是类结果,则结果为该结果。
- 否则,´M_1´ 和 ´M_2´ 必须都是项结果或都是类型结果。
- 如果它们是具有基础类型 ´U_1´ 和 ´U_2´ 以及稳定标志 ´s_1´ 和 ´s_2´ 的项结果,则结果是一个项结果,其基础类型为
meet(´U_1´, ´U_2´)
,其稳定标志为 ´s_1 \lor s_2´。 - 如果它们是具有基础类型定义 ´D_1´ 和 ´D_2´ 的类型结果,则结果是一个类型结果,其基础类型定义为
intersect(´D_1´, ´D_2´)
。
- 如果它们是具有基础类型 ´U_1´ 和 ´U_2´ 以及稳定标志 ´s_1´ 和 ´s_2´ 的项结果,则结果是一个项结果,其基础类型为
类型之间的关系
我们定义以下类型之间的关系。
名称 | 符号 | 解释 |
---|---|---|
一致性 | ´T <: U´ | 类型 ´T´ 符合(是类型 ´U´ 的子类型)。 |
等价 | ´T =:= U´ | ´T´ 和 ´U´ 彼此符合。 |
弱一致性 | ´T <:_w U´ | 增强基本数值类型的符合性。 |
兼容性 | 类型 ´T´ 在转换后符合类型 ´U´。 |
一致性
一致性关系 ´(<:)´ 是最小的关系,使得 ´S <: T´ 为真,如果以下任何条件成立。请注意,这些条件并非完全互斥的。
- ´S = T´(即,一致性根据定义是自反的)。
- ´S´ 是
Nothing
。 - ´T´ 是
AnyKind
。 - ´S´ 是一个具有基础类型 ´S_1´ 的稳定类型,并且 ´S_1 <: T´。
- ´S = p.x´ 且 ´T = q.x´ 是项指示符,并且
isSubPrefix(´p´, ´q´)
.
- ´S = p.X[S_1, ..., S_n]´ 且 ´T = q.X[T_1, ..., T_n]´ 是可能参数化的类型指示符,其中 ´n \geq 0´,并且
isSubPrefix(´p´, ´q´)
,并且- ´p.x´ 和 ´q.X´ 不是不同类的类类型指示符,并且
- 对于每个 ´i \in { 1, ..., n }´
- ´q.X´ 的第 ´i´ 个类型参数是协变的,并且 ´S_i <: T_i´ 3,或者
- ´q.X´ 的第 ´i´ 个类型参数是逆变的,并且 ´T_i <: S_i´ 3,或者
- ´q.X´ 的第 ´i´ 个类型参数是不变的,并且
- ´S_i´ 和 ´T_i´ 是类型,并且 ´S_i =:= T_i´,或者
- ´S_i´ 是一个类型,´T_i´ 是形式为 ´? >: L_2 <: H_2´ 的通配符类型参数,并且 ´L_2 <: S_i´ 并且 ´S_i <: H_2´,或者
- ´S_i´ 是一个通配符类型参数,形式为 ´? >: L_1 <: H_1´,´T_i´ 是一个通配符类型参数,形式为 ´? >: L_2 <: H_2´,并且 ´L_2 <: L_1´ 且 ´H_1 <: H_2´(即,´S_i´ 的“区间”包含在 ´T_i´ 的“区间”中)。
- ´T = q.C[T_1, ..., T_n]´,其中 ´n \geq 0´ 且
baseType(´S´, ´C´)
已定义,并且baseType(´S´, ´C´) ´<: T´
。 - ´S = p.X[S_1, ..., S_n]´,´p.X´ 是非类类型指示符,并且 ´H <: T´,其中 ´H´ 是 ´p.X´ 的基础类型定义的上限。
- ´S = p.C´ 且
´T = C´.this
,并且 ´C´ 是object
的隐藏类,并且- ´p = \epsilon´ 或 ´p´ 是包引用,或者
isSubPrefix(´p´, ´D´.this)
,其中 ´D´ 是 ´C´ 的封闭类。
´S = C´.this
且 ´T = q.C´,并且 ´C´ 是object
的隐藏类,并且- ´q = \epsilon´ 或 ´q´ 是包引用,或者
isSubPrefix(´D´.this, ´q´)
,其中 ´D´ 是 ´C´ 的封闭类。
- ´S = S_1 | S_2´ 且 ´S_1 <: T´ 且 ´S_2 <: T´。
- ´T = T_1 | T_2´,并且 ´S <: T_1´ 或 ´S <: T_2´。
- ´T = T_1 & T_2´ 且 ´S <: T_1´ 且 ´S <: T_2´。
- ´S = S_1 & S_2´,并且 ´S_1 <: T´ 或 ´S_2 <: T´。
´S = S_1´ @a
且 ´S_1 <: T´。´T = T_1´ @a
且 ´S <: T_1´(即,可以删除注释)。- ´T = q.X´,´q.X´ 是非类类型指示符,并且 ´S <: L´,其中 ´L´ 是 ´q.X´ 的基础类型定义的下限。
- ´S = p.X´,´p.X´ 是非类类型指示符,并且 ´H <: T´,其中 ´H´ 是 ´p.X´ 的基础类型定义的上限。
´S = [\pm a_1 >: L_1 <: H_1, ..., \pm a_n >: L_n <: H_n]´ =>> ´S_1´
且´T = [\pm b_1 >: M_1 <: G_1, ..., \pm b_n >: M_n <: G_n]´ =>> ´T_1´
,并且给定 ´\sigma = [b_1 := a_1, ..., b_n := a_n]´- ´S_1 <: \sigma T_1´,并且
- 对于每个 ´i \in { 1, ..., n }´
- ´a_i´ 的方差符合 ´b_i´ 的方差(´+´ 符合 ´+´ 和 ´\epsilon´,´-´ 符合 ´-´ 和 ´\epsilon´,´\epsilon´ 符合 ´\epsilon´),并且
- ´\sigma (>: M_i <: G_i)´ 包含在 ´>: L_i <: H_i´ 中(即,´L_i <: \sigma M_i´ 且 ´\sigma G_i <: H_i´)。
- ´S = p.X´ 且
´T = [\pm b_1 >: M_1 <: G_1, ..., \pm b_n >: M_n <: G_n]´ =>> ´T_1´
,并且 ´S´ 是具有 ´n´ 个类型参数的类型构造函数,并且´([\pm a_1 >: L_1 <: H_1, ..., \pm a_n >: L_n <: H_n]´ =>> ´S[a_1, ..., a_n]) <: T´
,其中 ´a_i´ 是 ´S´ 的类型参数的副本(即,我们可以 eta-扩展 ´S´ 以将其与类型 lambda 进行比较)。
´T = T_1´ { ´R´ }
且 ´S <: T_1´,并且给定 ´p = S´(如果 ´S´ 是稳定类型)或 ´p = ∃ \alpha : S´(否则)。´R =´ 类型 ´X >: L <: H´
且memberType(´p´, ´X´)
是一个类结果,其中 ´C´ 和 ´L <: p.C´ 以及 ´p.C <: H´,或者´R =´ 类型 ´X >: L_2 <: H_2´
且memberType(´p´, ´X´)
是一个类型结果,其边界为 ´>: L_1 <: H_1´ 且 ´L_2 <: L_1´ 以及 ´H_1 <: H_2´,或者´R =´ 值 ´X: T_2´
且memberType(´p´, ´X´)
是一个稳定项结果,其类型为 ´S_2´ 且 ´S_2 <: T_2´,或者´R =´ 定义 ´X: T_2´
且memberType(´p´, ´X´)
是一个项结果,其类型为 ´S_2´,其中 ´T_2´ 是一个类型且 ´S_2 <: T_2´,或者´R =´ 定义 ´X: T_2´
且memberType(´p´, ´X´)
是一个项结果,其方法类型为 ´S_2´,其中 ´T_2´ 是一个方法类型且matches(´S_2´, ´T_2´)
。
´S = S_1´ { ´R´ }
且 ´S_1 <: T´。´S =´ { ´\alpha´ => ´S_1´ }
且´T =´ { ´\beta´ => ´T_1´ }
且 ´S_1 <: [\beta := \alpha]T_1´。´T =´ { ´\beta´ => ´T_1´ }
且 ´S´ 是一个适当类型,但不是递归类型,且 ´p' <: [\beta := p]T_1´,其中- ´p´ 是 ´S´,如果 ´S´ 是一个稳定类型,否则是 ´∃ \alpha : S´,以及
- ´p'´ 是将 ´p´ 中任何顶级递归类型
{ ´\gamma´ => ´Z´ }
替换为 ´[\gamma := p]Z´ 的结果(TODO 更好地指定这一点)。
´S = (´=> ´S_1)´
且´T = (´=> ´T_1)´
且 ´S_1 <: T_1´。´S =´ scala.Null
且- ´T = q.C[T_1, ..., T_n]´,其中 ´n \geq 0´,且 ´C´ 不派生自
scala.AnyVal
,且 ´C´ 不是object
的隐藏类,或者 - ´T = q.x´ 是一个项指示符,其底层类型为 ´U´,且
scala.Null ´<: U´
,或者 ´T = T_1´ { ´R´ }
且scala.Null ´<: T_1´
,或者´T =´ { ´\beta´ => ´T_1´ }
且scala.Null ´<: T_1´
。
- ´T = q.C[T_1, ..., T_n]´,其中 ´n \geq 0´,且 ´C´ 不派生自
- ´S´ 是一个稳定类型,且 ´T = q.x´ 是一个项指示符,其底层类型为 ´T_1´,且 ´T_1´ 是一个稳定类型,且 ´S <: T_1´。
´S = S_1´ { ´R´ }
且 ´S_1 <: T´。´S =´ { ´\alpha´ => ´S_1´ }
且 ´S_1 <: T´。´T =´ scala.Tuple´_n[T_1, ..., T_n]´
其中 ´1 \leq n \leq 22´,并且´S <: T_1´ *: ... *: ´T_n´ *: scala.EmptyTuple
。
我们定义 isSubPrefix(´p´, ´q´)
,其中 ´p´ 和 ´q´ 是前缀,如下所示:
- 如果 ´p´ 和 ´q´ 都是类型,则 ´p <: q´。
- 否则,´p = q´(对于空前缀和包引用)。
我们定义 matches(´S´, ´T´)
,其中 ´S´ 和 ´T´ 是类型或方法类型,如下所示:
- 如果 ´S´ 和 ´T´ 是类型,则 ´S <: T´。
- 如果 ´S´ 和 ´T´ 是方法类型 ´(a_1: S_1, ..., a_n: S_n)S'´ 和 ´(b_1: T_1, ..., b_n: T_n)T'´,则 ´\sigma S_i =:= T_i´ 对于每个 ´i´ 并且
matches(´\sigma S'´, ´T'´)
,其中 ´\sigma = [a_1 := b_1, ..., a_n := b_n]´。 - 如果 ´S´ 和 ´T´ 是多态类型 ´[a_1 >: L_{s1} <: H_{s1}, ..., a_n >: L_{sn} <: H_{sn}]S'´ 和 ´[b_1 >: L_{t1} <: H_{t1}, ..., b_n >: L_{tn} <: H_{tn}]T'´,则 ´\sigma L_{si} =:= L_{ti}´ 和 ´\sigma H_{si} =:= H_{ti}´ 对于每个 ´i´ 并且
matches(´\sigma S'´, ´T'´)
,其中 ´\sigma = [a_1 := b_1, ..., a_n := b_n]´。
请注意,Scala 中的符合性 *不* 是可传递的。给定两个抽象类型 ´A´ 和 ´B´,以及一个在前缀 ´p´ 上可用的抽象 type ´C >: A <: B´
,我们有 ´A <: p.C´ 和 ´C <: p.B´,但不一定有 ´A <: B´。
最小上界和最大下界
´(<:)´ 关系在类型之间形成预序,即它是可传递的和自反的。这使我们能够根据该顺序定义一组类型的 *最小上界* 和 *最大下界*。
A
和B
的 *最小上界* 是最小的类型L
,使得A
<:L
并且B
<:L
。A
和B
的 *最大下界* 是最大的类型G
,使得G
<:A
并且G
<:B
。
通过构造,对于所有类型 A
和 B
,A
和 B
的最小上界是 A | B
,它们的最大下界是 A & B
。
等价
等价性定义为相互符合性。
´S =:= T´ 当且仅当 ´S <: T´ 和 ´T <: S´ 都成立。
弱一致性
在某些情况下,Scala 使用更通用的符合关系。类型 ´S´ *弱符合* 类型 ´T´,写成 ´S <:_w T´,如果 ´S <: T´ 或 ´S´ 和 ´T´ 都是原始数字类型,并且 ´S´ 在以下顺序中排在 ´T´ 之前。
*弱最小上界* 是相对于弱符合的最小上界。
兼容性
类型 ´T´ *兼容* 类型 ´U´,如果 ´T´(或其对应的函数类型)弱符合 类型 ´U´,在应用 eta 扩展 之后。如果 ´T´ 是方法类型,它将被转换为相应的函数类型。如果类型不弱符合,则按以下顺序检查备选方案
- 删除按名称修饰符:如果 ´U´ 的形状为
´=> U'´
(而 ´T´ 不是),则´T <:_w U'´
; - SAM 转换:如果 ´T´ 对应于函数类型,并且 ´U´ 声明一个单一抽象方法,其类型 对应 于函数类型 ´U'´,则
´T <:_w U'´
。 - 隐式转换:在范围内存在从 ´T´ 到 ´U´ 的隐式转换;
示例
通过 SAM 转换实现函数兼容性
给定以下定义
应用 foo((x: Int) => x.toString)
解析 为第一个重载,因为它更具体
Int => String
兼容ToString
-- 当期望类型为ToString
的值时,可以传递从Int
到String
的函数字面量,因为它将被 SAM 转换为该函数;ToString
不兼容Int => String
-- 当期望从Int
到String
的函数时,不能传递ToString
。
可实现性
类型 ´T´ *可实现* 当且仅当它被非空值填充。它被定义为
- 具有基础类型 ´U´ 的项指示符 ´p.x´ 可实现,如果 ´p´ 是 ´\epsilon´ 或包引用或可实现类型,并且
memberType(´p´, ´x´)
具有稳定标志,或者memberType(´p´, ´x´)
返回的类型是可实现的。
- 不是项指示符的稳定类型是可实现的。
- 另一个类型 ´T´ 可实现,如果
- ´T´ 是具体的,并且
- ´T´ 具有良好的边界。
具体类型 ´T´ 具有良好的边界,如果以下所有条件都适用
- 其所有非类类型成员都具有良好的边界,即它们的边界 ´L´ 和 ´H´ 使得 ´L <: H´,
- 其所有类型细化都具有良好的边界,并且
- 对于 ´T´ 的所有基类 ´C´
baseType(´T´, ´C´)
定义了一些结果 ´p.C[T_1, ..., T_n]´,并且- 对于所有 ´i \in { 1, ..., n }´,´T_i´ 是一个实类型,或者(当它是一个通配符类型参数时)它具有良好的边界。
注意:baseType(´T´, ´C´)
可能无法定义,因为 meet
计算可能会失败,无法合并前缀和/或不变类型参数。
类型擦除
如果类型包含类型参数或类型变量,则称为泛型类型。类型擦除是从(可能是泛型的)类型到非泛型类型的映射。我们用 ´|T|´ 表示类型 ´T´ 的擦除。擦除映射定义如下。内部计算在透明模式下执行,这会影响 memberType
对不透明类型别名的行为。
AnyKind
的擦除是Object
。- 非类类型指示符的擦除是其底层上限的擦除。
- 项指示符的擦除是其底层类型的擦除。
- 参数化类型
scala.Array´[T_1]´
的擦除是scala.Array´[|T_1|]´
。 - 所有其他参数化类型 ´T[T_1, ..., T_n]´ 的擦除是 ´|T|´。
- 稳定类型
´p´
的擦除是 ´p´ 的底层类型的擦除。 - 按名称类型
=> ´T_1´
的擦除是scala.Function0
。 - 带注释类型 ´T_1 a´ 的擦除是 ´|T_1|´。
- 细化类型
´T_1´ { ´R´ }
的擦除是 ´|T_1|´。 - 递归类型
{ ´\alpha´ => ´T_1´ }
和关联的递归 this 类型 ´\alpha´ 的擦除是 ´|T_1|´。 - 并集类型 ´S | T´ 的擦除是 ´S´ 和 ´T´ 的擦除的擦除最小上界 (elub)。
- 交集类型 ´S & T´ 的擦除是 ´S´ 和 ´T´ 的擦除的eglb(擦除最大下界)。
擦除 LUB 的计算方法如下
- 如果两个参数都是对象数组,则为元素类型的擦除 LUB 的数组
- 如果两个参数都是相同类型的基本类型数组,则为该基本类型的数组
- 如果一个参数是基本类型数组,另一个参数是对象数组,则为
Object
- 如果一个参数是数组,则为
Object
- 否则,为参数类的公共超类或特征 S,具有以下两个属性
- S 是最小的:没有其他公共超类或特征派生自 S,并且
- S 是最后的:在第一个参数类型 ´|A|´ 的线性化中,没有其他最小公共超类或特征位于 S 之后。选择最后一个的原因是,我们更喜欢类而不是特征,这会导致更可预测的字节码和 (?) 更快的动态调度。
´eglb(A, B)´ 的规则如下,以伪代码形式给出。