类型

Type                  ::=  FunType
                        |  TypeLambda
                        |  InfixType
FunType               ::=  FunTypeArgs ‘=>’ Type
                        |  TypeLambdaParams '=>' Type
TypeLambda            ::=  TypeLambdaParams ‘=>>’ Type
InfixType             ::=  RefinedType
                        |  RefinedTypeOrWildcard id [nl] RefinedTypeOrWildcard {id [nl] RefinedTypeOrWildcard}
RefinedType           ::=  AnnotType {[nl] Refinement}
AnnotType             ::=  SimpleType {Annotation}
SimpleType            ::=  SimpleLiteral
                        |  SimpleType1
SimpleType1           ::=  id
                        |  Singleton ‘.’ id
                        |  Singleton ‘.’ ‘type’
                        |  ‘(’ TypesOrWildcards ‘)’
                        |  Refinement
                        |  SimpleType1 TypeArgs
                        |  SimpleType1 ‘#’ id
Singleton             ::=  SimpleRef
                        |  SimpleLiteral
                        |  Singleton ‘.’ id
SimpleRef             ::=  id
                        |  [id ‘.’] ‘this’
                        |  [id ‘.’] ‘super’ [‘[’ id ‘]’] ‘.’ id
ParamType             ::=  [‘=>’] ParamValueType
ParamValueType        ::=  ParamValueType [‘*’]
TypeArgs              ::=  ‘[’ TypesOrWildcards ‘]’
Refinement            ::=  :<<< [RefineDef] {semi [RefineDef]} >>>

FunTypeArgs           ::=  InfixType
                        |  ‘(’ [ FunArgTypes ] ‘)’
                        |  FunParamClause
FunArgTypes           ::=  FunArgType { ‘,’ FunArgType }
FunArgType            ::=  Type
                        |  ‘=>’ Type
FunParamClause        ::=  ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’
TypedFunParam         ::=  id ‘:’ Type

TypeLambdaParams      ::=  ‘[’ TypeLambdaParam {‘,’ TypeLambdaParam} ‘]’
TypeLambdaParam       ::=  {Annotation} (id | ‘_’) [TypeParamClause] TypeBounds
TypeParamClause       ::=  ‘[’ VariantTypeParam {‘,’ VariantTypeParam} ‘]’
VariantTypeParam      ::=  {Annotation} [‘+’ | ‘-’] (id | ‘_’) [TypeParamClause] TypeBounds

RefineDef             ::=  ‘val’ ValDef
                        |  ‘def’ DefDef
                        |  ‘type’ {nl} TypeDef

TypeBounds            ::=  [‘>:’ Type] [‘<:’ Type]

TypesOrWildcards      ::=  TypeOrWildcard {‘,’ TypeOrWildcard}
TypeOrWildcard        ::=  Type
                        |  WildcardType
RefinedTypeOrWildcard ::=  RefinedType
                        |  WildcardType
WildcardType          ::=  (‘?‘ | ‘_‘) TypeBounds

上面的语法描述了用户代码中可以编写的类型的具体语法。Scala 类型系统中对类型的语义操作最好用内部类型来定义,内部类型是从具体类型语法中反糖化的。

内部类型

以下抽象语法定义了内部类型的形状。在本规范中,除非另有说明,"类型" 指的是内部类型。内部类型抽象掉了诸如优先级和分组之类的无关细节,并包含无法使用具体语法直接表达的类型形状。它们还包含对复杂具体语法类型的简化、分解形状,例如细化类型。

Type              ::=  ‘AnyKind‘
                    |  ‘Nothing‘
                    |  TypeLambda
                    |  DesignatorType
                    |  ParameterizedType
                    |  ThisType
                    |  SuperType
                    |  LiteralType
                    |  ByNameType
                    |  AnnotatedType
                    |  RefinedType
                    |  RecursiveType
                    |  RecursiveThis
                    |  UnionType
                    |  IntersectionType
                    |  SkolemType

TypeLambda        ::=  ‘[‘ TypeParams ‘]‘ ‘=>>‘ Type
TypeParams        ::=  TypeParam {‘,‘ TypeParam}
TypeParam         ::=  ParamVariance id TypeBounds
ParamVariance     ::=  ε | ‘+‘ | ‘-‘

DesignatorType    ::=  Prefix ‘.‘ id
Prefix            ::=  Type
                    |  PackageRef
                    |  ε
PackageRef        ::=  id {‘.‘ id}

ParameterizedType ::=  Type ‘[‘ TypeArgs ‘]‘
TypeArgs          ::=  TypeArg {‘,‘ TypeArg}
TypeArg           ::=  Type
                    |  WilcardTypeArg
WildcardTypeArg   ::=  ‘?‘ TypeBounds

ThisType          ::=  classid ‘.‘ ‘this‘
SuperType         ::=  classid ‘.‘ ‘super‘ ‘[‘ classid ‘]‘
LiteralType       ::=  SimpleLiteral
ByNameType        ::=  ‘=>‘ Type
AnnotatedType     ::=  Type Annotation

RefinedType       ::=  Type ‘{‘ Refinement ‘}‘
Refinement        ::=  ‘type‘ id TypeAliasOrBounds
                    |  ‘def‘ id ‘:‘ TypeOrMethodic
                    |  ‘val‘ id ‘:‘ Type

RecursiveType     ::=  ‘{‘ recid ‘=>‘ Type ‘}‘
RecursiveThis     ::=  recid ‘.‘ ‘this‘

UnionType         ::=  Type ‘|‘ Type
IntersectionType  ::=  Type ‘&‘ Type

SkolemType        ::=  ‘∃‘ skolemid ‘:‘ Type

TypeOrMethodic    ::=  Type
                    |  MethodicType
MethodicType      ::=  MethodType
                    |  PolyType

MethodType        ::=  ‘(‘ MethodTypeParams ‘)‘ TypeOrMethodic
MethodTypeParams  ::=  ε
                    |  MethodTypeParam {‘,‘ MethodTypeParam}
MethodTypeParam   ::=  id ‘:‘ Type

PolyType          ::=  ‘[‘ PolyTypeParams ‘]‘ TypeOrMethodic
PolyTypeParams    ::=  PolyTypeParam {‘,‘ PolyTypeParam}
PolyTypeParam     ::=  ‘id‘ TypeBounds

TypeAliasOrBounds ::=  TypeAlias
                    |  TypeBounds
TypeAlias         ::=  ‘=‘ Type
TypeBounds        ::=  ‘<:‘ Type ‘>:‘ Type

具体类型到内部类型的转换

具体类型被递归地转换为内部类型,或反糖化。大多数具体类型的形状与内部类型的形状一一对应。我们将在下文中详细说明其他类型的转换。

中缀类型

InfixType     ::=  CompoundType {id [nl] CompoundType}

具体中缀类型 ´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.&,它们分别代表 并集类型和交集类型

函数类型

Type              ::=  FunTypeArgs ‘=>’ Type
FunTypeArgs       ::=  InfixType
                    |  ‘(’ [ FunArgTypes ] ‘)’
                    |  FunParamClause
FunArgTypes       ::=  FunArgType { ‘,’ FunArgType }
FunArgType        ::=  Type
                    |  ‘=>’ Type
FunParamClause    ::=  ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’
TypedFunParam     ::=  id ‘:’ Type

具体函数类型 ´(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´] 的简写。

此类类类型表现得好像它们是以下特性的实例

trait Function´_n´[-´T_1´, ..., -´T_n´, +´R´]:
  def apply(´x_1´: ´T_1´, ..., ´x_n´: ´T_n´): ´R´

它们的精确超类型和实现可以在本文档的标准库页面中的 函数类部分 中查询。

依赖函数类型 是参数已命名并且可以在结果类型中引用的函数类型。在具体类型 ´(x_1: T_1, ..., x_n: T_n) \Rightarrow R´ 中,´R´ 可以引用参数 ´x_i´,特别是形成路径依赖类型。它转换为内部 精炼类型

scala.Function´_n´[´T_1´, ..., ´T_n´, ´S´] {
  def apply(´x_1´: ´T_1´, ..., ´x_n´: ´T_n´): ´R´
}

其中 ´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´。它转换为内部精炼类型

scala.PolyFunction {
  def apply[´a_1 >: L_1 <: H_1, ..., a_n >: L_1 <: H_1´](´x_1´: ´T_1´, ..., ´x_n´: ´T_n´): ´R´
}

元组类型

SimpleType1           ::=  ...
                        |  ‘(’ TypesOrWildcards ‘)’

元组类型 ´(T_1, ..., T_n)´,其中 ´n \geq 2´,是类型 ´T_1´ *: ... *: ´T_n´ *: scala.EmptyTuple 的语法糖,它本身是一系列嵌套的 infix 类型,这些类型是 *:[´T_1´, *:[´T_2´, ... *:[´T_n´, scala.EmptyTuple]]] 的语法糖。´T_i´ 可以是通配符类型参数。

注意

具体细化类型

RefinedType           ::=  AnnotType {[nl] Refinement}
SimpleType1           ::=  ...
                        |  Refinement
Refinement            ::=  :<<< [RefineDef] {semi [RefineDef]} >>>

RefineDef             ::=  ‘val’ ValDef
                        |  ‘def’ DefDef
                        |  ‘type’ {nl} TypeDef

在类型的具体语法中,细化可以包含多个细化定义。它们都必须是抽象的。此外,细化定义可以相互引用,也可以引用父类型的成员,即它们可以访问 this

在内部类型中,每个细化定义恰好定义一个细化定义,并且对 this 的引用必须在递归类型中显式地进行。

从具体语法到抽象语法的转换如下

  1. 创建一个新的递归 this 名称 ´\alpha´。
  2. 将细化定义中对 this 的所有隐式或显式引用替换为 ´\alpha´。
  3. 为每个细化定义创建嵌套的 细化类型
  4. 除非 ´\alpha´ 实际上从未被使用过,否则将结果包装在 递归类型 { ´\alpha´ => ´...´ } 中。

具体类型 lambda

TypeLambda            ::= TypeLambdaParams ‘=>>’ Type
TypeLambdaParams      ::=  ‘[’ TypeLambdaParam {‘,’ TypeLambdaParam} ‘]’
TypeLambdaParam       ::=  {Annotation} (id | ‘_’) [TypeParamClause] TypeBounds
TypeParamClause       ::=  ‘[’ VariantTypeParam {‘,’ VariantTypeParam} ‘]’
VariantTypeParam      ::=  {Annotation} [‘+’ | ‘-’] (id | ‘_’) [TypeParamClause] TypeBounds

在具体类型 lambda 参数的顶层,不允许使用方差注释。但是,在内部类型中,所有类型 lambda 参数都有显式的方差注释。

将具体类型 lambda 转换为内部类型 lambda 时,每个类型参数的方差是根据其在类型 lambda 主体中的用法推断出来的。

定义

从这里开始,我们默认情况下引用内部类型。

种类

Scala 类型系统本质上是高阶的。类型要么是适当类型,要么是类型构造函数,要么是多阶类型

所有类型都相对于一个 一致性 关系 ´<:´ 存在于单个格中。顶层类型AnyKind底层类型Nothing:所有类型都符合 AnyKind,而 Nothing 符合所有类型。它们可以分别用 基本类型别名 scala.AnyKindscala.Nothing 来引用。

类型可以是具体的或抽象的。抽象类型 ´T´ 始终具有下界和上界 ´L´ 和 ´H´,使得 ´L >: T´ 和 ´T <: H´。具体类型 ´T´ 被认为具有自身作为下界和上界。

类型的种类由其(传递)上界指示

因此,AnyKind 本身是多态的。Nothing通用种类的:它同时具有所有种类,因为它符合所有类型。

使用这种表示,很少需要显式地谈论类型的种类。通常,类型的种类是通过其边界隐式地确定的。

另一种看待它的方式是类型边界就是种类。它们表示类型的集合:´>: L <: H´ 表示满足 ´L <: T´ 和 ´T <: H´ 的类型 ´T´ 的集合。类型的集合可以被视为类型的类型,即种类

约定

类型边界在形式上始终是 ´>: L <: H´ 的形式。按照惯例,我们可以省略写入中的任一或两个边界。

这些约定对应于具体语法中的默认值。

适当类型

适当类型也称为值类型,因为它们表示的集合。

稳定类型 是包含恰好一个非 null 值的值类型。稳定类型可以用作命名 指示符类型 中的前缀。稳定类型是

每个稳定类型 ´T´ 都是具体的,并且具有一个底层类型 ´U´,使得 ´T <: U´。

类型构造函数

每个类型构造函数都对应一个推断类型参数子句,该子句按如下方式计算

类型定义

一个类型定义 ´D´ 代表一个 type 成员定义的右侧或类型参数的边界。它可以是

所有类型定义都有一个下界 ´L´ 和一个上界 ´H´,它们都是类型。对于类型别名,´L = H = U´。

类型参数的类型定义永远不是类型别名。

类型

类型 Lambda

TypeLambda     ::=  ‘[‘ TypeParams ‘]‘ ‘=>>‘ Type
TypeParams     ::=  TypeParam {‘,‘ TypeParam}
TypeParam      ::=  ParamVariance id TypeBounds
ParamVariance  ::=  ε | ‘+‘ | ‘-‘

一个形式为 [´\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 的主体推断出来的,尽可能通用。

示例
type Lst = [T] =>> List[T] // T is inferred to be covariant with bounds >: Nothing <: Any
type Fn = [A <: Seq[?], B] =>> (A => B) // A is inferred to be contravariant, B covariant

val x: Lst[Int] = List(1) // ok, Lst[Int] expands to List[Int]
val f: Fn[List[Int], Int] = (x: List[Int]) => x.head // ok

val g: Fn[Int, Int] = (x: Int) => x // error: Int does not conform to the bound Seq[?]

def liftPair[F <: [T] =>> Any](f: F[Int]): Any = f
liftPair[Lst](List(1)) // ok, Lst <: ([T] =>> Any)

指示符类型

DesignatorType    ::=  Prefix ‘.‘ id
Prefix            ::=  Type
                    |  PackageRef
                    |  ε
PackageRef        ::=  id {‘.‘ id}

指示符类型(或简称指示符)是对定义的引用。术语指示符引用术语定义,而类型指示符引用类型定义。

在抽象语法中,id 保留了它是术语还是类型。在具体语法中,id 引用类型指示符,而 id.type 引用术语指示符。在这种情况下,术语指示符通常被称为单例类型

具有空前缀 ´\epsilon´ 的指示符称为直接指示符。它们引用作用域中可用的本地定义

直接指示符的 id 在抽象语法中受到保护,防止意外遮蔽。它们保留了所引用确切定义的标识,而不是依赖于基于作用域的名称解析。 1

´\epsilon´ 前缀不能在具体语法中写出。而是使用裸 id,并根据作用域解析。

命名指示符引用非空前缀的成员定义

项指示符

引用项定义 t 的项指示符 ´p.x´ 具有底层类型 ´U´。如果 ´p = \epsilon´ 或 ´p´ 是包引用,则底层类型 ´U´ 是 t声明类型,并且 ´p.x´ 是稳定类型,当且仅当 tvalobject 定义。否则,底层类型 ´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´ 本身是具体的(分别为稳定的)。

参数化类型

ParameterizedType ::=  Type ‘[‘ TypeArgs ‘]‘
TypeArgs          ::=  TypeArg {‘,‘ TypeArg}
TypeArg           ::=  Type
                    |  WilcardTypeArg
WildcardTypeArg   ::=  ‘?‘ TypeBounds

参数化类型 ´T[T_1, ..., T_n]´ 由类型构造函数 ´T´ 和类型参数 ´T_1, ..., T_n´ 组成,其中 ´n \geq 1´。参数化类型是良构的,如果

´T[T_1, ..., T_n]´ 是一个参数化类类型,当且仅当 ´T´ 是一个 类类型。所有参数化类类型都是值类型。

在通配符类型参数的具体语法中,如果省略了两个边界,则实际边界从目标类型构造函数(必须是具体的)中对应类型参数的边界推断。如果只省略了一个边界,则像往常一样使用 NothingAny

同样在具体语法中,出于兼容性原因,可以使用 _ 代替 ?,含义相同。这种替代方案将在将来被弃用,并且在 -source:future 下已经过时。

简化规则

在协变或逆变位置使用的通配符类型参数始终可以简化为普通类型。

令 ´T[T_1, ..., T_n]´ 为具体类型构造函数的参数化类型。然后,在 ´i´ 位置应用通配符类型参数 ´? >: L <: H´ 遵循以下等价关系

示例参数化类型

给定部分类型定义

class TreeMap[A <: Comparable[A], B] { ... }
class List[+A] { ... }
class I extends Comparable[I] { ... }

class F[M[A], X] { ... } // M[A] desugars to M <: [A] =>> Any
class S[K <: String] { ... }
class G[M[Z <: I], I] { ... } // M[Z <: I] desugars to M <: [Z <: I] =>> Any

以下参数化类型是格式良好的

TreeMap[I, String]
List[I]
List[List[Boolean]]

F[List, Int]
F[[X] =>> List[X], Int]
G[S, String]

List[?] // ? inferred as List[_ >: Nothing <: Any], equivalent to List[Any]
List[? <: String] // equivalent to List[String]
S[? <: String]
F[?, Boolean] // ? inferred as ? >: Nothing <: [A] =>> Any

以下类型是格式错误的

TreeMap[I]            // illegal: wrong number of parameters
TreeMap[List[I], Int] // illegal: type parameter not within bound
List[[X] => List[X]]

F[Int, Boolean]       // illegal: Int is not a type constructor
F[TreeMap, Int]       // illegal: TreeMap takes two parameters,
                      //   F expects a constructor taking one
F[[X, Y] => (X, Y)]
G[S, Int]             // illegal: S constrains its parameter to
                      //   conform to String,
                      // G expects type constructor with a parameter
                      //   that conforms to Int

以下代码也包含格式错误的类型

trait H[F[A]]:  // F[A] desugars to F <: [A] =>> Any, which is abstract
  def f: F[_]   // illegal : an abstract type constructor
                // cannot be applied to wildcard arguments.

此类型

ThisType  ::=  classid ‘.‘ ‘this‘

此类型 ´C´.this 表示类 ´C´ 中 ´C´ 的 this 值。

此类型通常隐式地作为 指定类型 的前缀出现,这些指定类型引用 ´C´ 的成员。它们在类型系统中扮演着特殊的角色,因为它们会受到类型上的 从...看 操作的影响。

此类型是稳定类型。´C´.this 的底层类型是 ´C´ 的 自身类型

超类型

SuperType  ::=  classid ‘.‘ ‘super‘ ‘[‘ classid ‘]‘

超类型 ´C´.super[´D´] 表示类 CCthis 值,但“扩展”到仅看到来自父类或特征 ´D´ 的成员。

超类型存在是为了与 Scala 2 兼容,Scala 2 允许对内部类进行遮蔽。在仅限 Scala 3 的上下文中,超类型始终可以用相应的 此类型 替换。因此,我们在本规范中省略了对超类型的进一步讨论。

字面量类型

LiteralType  ::=  SimpleLiteral

字面量类型 lit 表示单个字面量值 lit。因此,类型断言 1: 1 为字面量值 1 提供最精确的类型:字面量类型 1

在运行时,表达式 e 被认为具有字面类型 lit,如果 e == lit。具体来说,e.isInstanceOf[lit]e match { case _ : lit => } 的结果由评估 e == lit 决定。

字面类型可用于所有原始类型,以及 String。但是,只有 IntLongFloatDoubleBooleanCharString 的字面类型可以在具体语法中表达。

字面类型是稳定类型。它们的底层类型是包含其值的原始类型。

示例
val x: 1 = 1
val y: false = false
val z: false = y
val int: Int = x

val badX: 1 = int       // error: Int is not a subtype of 1
val badY: false = true  // error: true is not a subtype of false

按名类型

ByNameType  ::=  ‘=>‘ Type

按名类型 ´=> T´ 表示按名参数的声明类型。按名类型只能作为方法类型中参数的类型出现,以及作为 参数化类型 中的类型参数。

带注释的类型

AnnotatedType  ::=  Type Annotation

带注释的类型 ´T a´ 将 注释 ´a´ 附加到类型 ´T´ 上。

示例

以下类型将 @suspendable 注释添加到类型 String

String @suspendable

细化类型

RefinedType  ::=  Type ‘{‘ Refinement ‘}‘
Refinement   ::=  ‘type‘ id TypeAliasOrBounds
               |  ‘def‘ id ‘:‘ TypeOrMethodic
               |  ‘val‘ id ‘:‘ Type

细化类型 ´T { R }´ 表示属于 ´T´ 并且也具有符合细化 ´R´ 的成员的值集。

如果细化类型 ´T { R }´ 是良构的,则

作为最后一条规则的例外,如果 ´T <:´ scala.PolyFunction 并且 ´id´ 是名称 apply,则允许多态方法类型细化。

如果细化 ´R´ 不覆盖 ´T´ 的任何成员,并且不是 scala.PolyFunction 例外的出现,则该细化被称为“结构化” 2

注意:由于细化不定义,因此无法使用this 类型来引用父类型 ´T´ 中的术语和类型成员。当细化类型的表面语法进行此类引用时,递归类型将包装细化类型,通过递归 this 类型提供对自身成员的访问。

示例

给定以下类定义

trait T:
  type X <: Option[Any]
  def foo: Any
  def fooPoly[A](x: A): Any

trait U extends T:
  override def foo: Int
  override def fooPoly[A](x: A): A

trait V extends T
  type X = Some[Int]
  def bar: Int
  def barPoly[A](x: A): A

我们得到以下一致性关系

以下细化类型格式不正确

递归类型

RecursiveType  ::=  ‘{‘ recid ‘=>‘ Type ‘}‘
RecursiveThis  ::=  recid ‘.‘ ‘this‘

形式为 { ´\alpha´ => ´T´ }递归类型表示与 ´T´ 相同的值,同时为 ´T´ 提供对其递归 this 类型 ´\alpha´ 的访问。

递归类型不能直接在具体语法中表达。当具体语法中的细化类型包含需要访问 this 值的细化时,它们会根据需要创建。每个递归类型都定义一个唯一的自引用 ´\alpha´,与系统中的任何其他递归类型不同。

递归类型可以在子类型化期间根据需要展开,将对 ´\alpha´ 的引用替换为对一致性关系另一侧的稳定引用。

示例

给定细化类型部分中的类定义,我们可以使用以下细化类型在源语法中编写

T { def foo: X }
// equivalent to
T { def foo: this.X }

此类型不能直接作为单独的细化类型表达,因为细化无法访问 this 值。相反,在类型的抽象语法中,它被转换为 { ´\alpha´ => ´T´ { def foo: ´\alpha´.X } }

给定以下定义

trait Z extends T:
  type X = Option[Int]
  def foo: Option[Int] = Some(5)

val z: Z

我们可以检查 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]

联合类型和交集类型

UnionType         ::=  Type ‘|‘ Type
IntersectionType  ::=  Type ‘&‘ Type

从语法上讲,类型 S | TS & T 是中缀类型,其中中缀运算符分别是 |&(参见 中缀类型)。

但是,在本规范中,´S | T´ 和 ´S & T´ 分别指代联合类型交集类型的底层核心概念。

一致性规则 中关于联合类型和交集类型的规则,我们可以证明 ´&´ 和 ´|´ 是可交换的和可结合的。此外, 是可分配的。对于任何类型 ´A´、´B´ 和 ´C´,以下所有关系都成立

如果 ´C´ 是一个协变或逆变类型构造器,´C[A] & C[B]´ 可以使用以下规则简化

上述两个规则的右到左有效性可以从协变和逆变的定义以及联合类型和交集类型的一致性规则推导出来

联合类型的合并

在某些情况下,可能需要将联合类型扩展为非联合类型。为此,我们将联合类型 ´T_1 | ... | T_n´ 的合并定义为 ´T_1, ..., T_n´ 的基类实例的最小交集类型。请注意,联合类型仍然可能作为类型参数出现在结果类型中,这保证了合并始终是有限的。

例如,给定

trait C[+T]
trait D
trait E
class A extends C[A] with D
class B extends C[B] with D with E

´A | B´ 的连接是 ´C[A | B] & D´

Skolem 类型

SkolemType  ::=  ‘∃‘ skolemid ‘:‘ Type

Skolem 类型不能直接在具体语法中写出。此外,虽然它们是正确的类型,但它们永远不会被推断为是项定义(valvardef)类型的组成部分。它们只在子类型推导过程中临时使用。

Skolem 类型是稳定类型。形式为 ´∃ \alpha : T´ 的 Skolem 类型表示对类型为 ´T´ 的未知值的稳定引用。标识符 ´\alpha´ 在每次创建 Skolem 类型时都会被唯一地选择。但是,由于 Skolem 类型是稳定的,因此它可以在其他类型中的多个出现位置被替换。当通过替换“复制”时,所有副本都保留相同的 ´\alpha´,因此是等价的。

方法类型

TypeOrMethodic    ::=  Type
                    |  MethodicType
MethodicType      ::=  MethodType
                    |  PolyType

方法类型不是真正的类型。它们不是类型格的一部分。

但是,它们与类型共享一些元属性。特别是,当包含在进行某些替换的其他类型中时,替换会传递到方法类型中的类型。因此,将它们视为类型本身通常很方便。

方法类型用作至少有一个项或类型参数列表的 def 定义的“声明类型”。

方法类型

MethodType        ::=  ‘(‘ MethodTypeParams ‘)‘ TypeOrMethodic
MethodTypeParams  ::=  ε
                    |  MethodTypeParam {‘,‘ MethodTypeParam}
MethodTypeParam   ::=  id ‘:‘ Type

方法类型在内部表示为 ´(\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)´。

方法类型不存在作为值的类型。如果方法名称用作值,则其类型将 隐式转换为 对应的函数类型。

示例

定义

def a: Int
def b (x: Int): Boolean
def c (x: Int) (y: String, z: String): String

产生类型

a: Int
b: (Int) Boolean
c: (Int) (String, String) String

多态方法类型

PolyType          ::=  ‘[‘ PolyTypeParams ‘]‘ TypeOrMethodic
PolyTypeParams    ::=  PolyTypeParam {‘,‘ PolyTypeParam}
PolyTypeParam     ::=  ‘id‘ TypeBounds

多态方法类型,简称 多态类型,在内部表示为 [´\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´ 的结果。

示例

定义

def empty[A]: List[A]
def union[A <: Comparable[A]] (x: Set[A], xs: Set[A]): Set[A]

产生类型

empty : [A >: Nothing <: Any] List[A]
union : [A >: Nothing <: Comparable[A]] (x: Set[A], xs: Set[A]) Set[A]

类型操作

本节定义了关于类型和方法类型的几个元函数。

这些元函数是相互递归的。

基本类型

元函数 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´。

以上定义使用了以下辅助函数。

superType(´T´) 计算 ´T´ 的“下一个上界”,如果存在

请注意,superType 的情况不会相互重叠,也不会与除基于 superType 的情况以外的任何 baseType 情况重叠。因此,baseType 的情况也不会相互重叠。这使得 baseType 成为一个算法上的偏函数。

meet(´p.C[T_1, ..., T_n]´, ´q.C[U_1, ..., U_n]´) 计算两个(参数化)类类型的交集,它们属于同一个类,而 join 计算并集

我们将 meet(´T_1, ..., T_n´) 泛化为一个序列,如下所示

示例

给定以下定义

trait Iterable[+A]
trait List[+A] extends Iterable[A]
trait Map[K, +V] extends Iterable[(K, V)]
trait Foo

我们有以下 baseType 结果

来源

元函数 `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]´` 如下

为了方便起见,我们将 `asSeenFrom` 泛化到类型定义 `D`。

成员类型

元函数 `memberType(´T´, ´id´, ´p´)`,其中 `T` 是一个适当的类型,`id` 是一个术语或类型标识符,`p` 是一个稳定类型,它找到一个类型的成员(`T.id`)并计算其从前缀 `p` 的角度看到的底层类型(对于术语)或类型定义(对于类型)。对于术语,它还计算术语是否稳定。`memberType` 是计算命名设计器类型底层类型底层类型定义的基本操作。

`memberType` 的结果 `M` 是以下之一

作为简写,我们定义 `memberType(´T´, ´id´)` 与 `memberType(´T´, ´id´, ´T´)` 相同,当 `T` 是一个稳定类型时。

我们定义 memberType(´T´, ´id´, ´p´) 如下

我们定义辅助函数 mergeMemberType(´M_1´, ´M_2´) 如下

类型之间的关系

我们定义以下类型之间的关系。

名称 符号 解释
一致性 ´T <: U´ 类型 ´T´ 符合(是类型 ´U´ 的子类型)。
等价 ´T =:= U´ ´T´ 和 ´U´ 彼此符合。
弱一致性 ´T <:_w U´ 增强基本数值类型的符合性。
兼容性 类型 ´T´ 在转换后符合类型 ´U´。

一致性

一致性关系 ´(<:)´ 是最小的关系,使得 ´S <: T´ 为真,如果以下任何条件成立。请注意,这些条件并非完全互斥的。

我们定义 isSubPrefix(´p´, ´q´),其中 ´p´ 和 ´q´ 是前缀,如下所示:

我们定义 matches(´S´, ´T´),其中 ´S´ 和 ´T´ 是类型或方法类型,如下所示:

请注意,Scala 中的符合性 *不* 是可传递的。给定两个抽象类型 ´A´ 和 ´B´,以及一个在前缀 ´p´ 上可用的抽象 type ´C >: A <: B´,我们有 ´A <: p.C´ 和 ´C <: p.B´,但不一定有 ´A <: B´。

最小上界和最大下界

´(<:)´ 关系在类型之间形成预序,即它是可传递的和自反的。这使我们能够根据该顺序定义一组类型的 *最小上界* 和 *最大下界*。

通过构造,对于所有类型 ABAB 的最小上界是 A | B,它们的最大下界是 A & B

等价

等价性定义为相互符合性。

´S =:= T´ 当且仅当 ´S <: T´ 和 ´T <: S´ 都成立。

弱一致性

在某些情况下,Scala 使用更通用的符合关系。类型 ´S´ *弱符合* 类型 ´T´,写成 ´S <:_w T´,如果 ´S <: T´ 或 ´S´ 和 ´T´ 都是原始数字类型,并且 ´S´ 在以下顺序中排在 ´T´ 之前。

Byte  ´<:_w´ Short
Short ´<:_w´ Int
Char  ´<:_w´ Int
Int   ´<:_w´ Long
Long  ´<:_w´ Float
Float ´<:_w´ Double

*弱最小上界* 是相对于弱符合的最小上界。

兼容性

类型 ´T´ *兼容* 类型 ´U´,如果 ´T´(或其对应的函数类型)弱符合 类型 ´U´,在应用 eta 扩展 之后。如果 ´T´ 是方法类型,它将被转换为相应的函数类型。如果类型不弱符合,则按以下顺序检查备选方案

示例

通过 SAM 转换实现函数兼容性

给定以下定义

def foo(x: Int => String): Unit
def foo(x: ToString): Unit

trait ToString { def convert(x: Int): String }

应用 foo((x: Int) => x.toString) 解析 为第一个重载,因为它更具体

可实现性

类型 ´T´ *可实现* 当且仅当它被非空值填充。它被定义为

具体类型 ´T´ 具有良好的边界,如果以下所有条件都适用

注意:baseType(´T´, ´C´) 可能无法定义,因为 meet 计算可能会失败,无法合并前缀和/或不变类型参数。

类型擦除

如果类型包含类型参数或类型变量,则称为泛型类型。类型擦除是从(可能是泛型的)类型到非泛型类型的映射。我们用 ´|T|´ 表示类型 ´T´ 的擦除。擦除映射定义如下。内部计算在透明模式下执行,这会影响 memberType 对不透明类型别名的行为。

擦除 LUB 的计算方法如下

´eglb(A, B)´ 的规则如下,以伪代码形式给出。

eglb(scala.Array[A], JArray[B]) = scala.Array[eglb(A, B)]
eglb(scala.Array[T], _)         = scala.Array[T]
eglb(_, scala.Array[T])         = scala.Array[T]
eglb(A, B)                      = A                     if A extends B
eglb(A, B)                      = B                     if B extends A
eglb(A, _)                      = A                     if A is not a trait
eglb(_, B)                      = B                     if B is not a trait
eglb(A, _)                      = A                     // use first

  1. 在文献中,这通常通过德布鲁因索引或在需要时通过 alpha 重命名来实现。在具体的实现中,这通常通过在符号表中保留符号引用来实现。 

  2. 对结构化定义的成员(方法调用或访问值或变量)的引用可能会生成二进制代码,其速度明显慢于等效的非结构化成员代码。 

  3. 在这些情况下,如果 T_i 和/或 U_i 是通配符类型参数,则参数化类型的简化规则允许将其简化为实际类型。