类型类别

类型类是一种特殊的伪类型,可用于匹配重载决策或 is 运算符中的类型。 Nim支持以下内置类型类:

类型匹配
object任意对象类型
tuple任意元组类型
enum任意枚举
proc任意过程类型
ref任意 ref 类型
ptr任意 ptr 类型
var任意 var 类型
distinct任意distinct类型
array任意数组array类型
set任意set类型
seq任意seq类型
auto任意类型
anydistinct auto (见下方)

此外,每个泛型类型都会自动创建一个与通用类型的任何实例化相匹配的相同名称的类型类。

可以使用标准布尔运算符组合类型类,以形成更复杂的类型类:

  1. # 创建一个匹配所有元组和对象类型的类型类
  2. type RecordType = tuple or object
  3.  
  4. proc printFields(rec: RecordType) =
  5. for key, value in fieldPairs(rec):
  6. echo key, " = ", value

以这种方式使用类型类的过程被认为是 隐式通用的 。 对于程序中使用的每个唯一的param类型组合,它们将被实例化一次。

虽然类型类的语法看起来类似于ML的语言中的ADT /代数数据类型,但应该理解类型类是类型实例化时强制执行的静态约束。 类型类不是真正的类型,而是一个提供通用“检查”的系统,最终将 解析 为某种单一类型。 与对象变体或方法不同,类型类不允许运行时类型动态。

例如,以下内容无法编译:

  1. type TypeClass = int | string
  2. var foo: TypeClass = 2 # foo的类型在这里解析为int
  3. foo = "this will fail" # 错误在这里,因为foo是一个int

Nim允许将类型类和常规类型指定为 类型限制 泛型类型参数:

  1. proc onlyIntOrString[T: int|string](x, y: T) = discard
  2.  
  3. onlyIntOrString(450, 616) # 合法
  4. onlyIntOrString(5.0, 0.0) # 类型不匹配
  5. onlyIntOrString("xy", 50) # 不合法因为 'T' 不能同时是两种类型

默认情况下,在重载解析期间,每个命名类型类将仅绑定到一个具体类型。 我们称这样的类类为 绑定一次 类型。 以下是直接从系统模块中抽取的用于展示的示例:

  1. proc `==`*(x, y: tuple): bool =
  2. ## 要求 `x` and `y` 是同样的元组类型
  3. ## 从 `x` 和 `y` 部分中提升的元组泛型 ``==`` 操作符。
  4. result = true
  5. for a, b in fields(x, y):
  6. if a != b: result = false

或者,可以将 distinct 类型修饰符应用于类型类,以允许与类型类匹配的每个参数绑定到不同的类型。 这种类型类称为 绑定多次 类型

使用隐式通用样式编写的过程通常需要引用匹配泛型类型的类型参数。 可以使用点语法轻松访问它们:

  1. type Matrix[T, Rows, Columns] = object
  2. ...
  3.  
  4. proc `[]`(m: Matrix, row, col: int): Matrix.T =
  5. m.data[col * high(Matrix.Columns) + row]

或者,当使用匿名或不同类型类时,可以在proc参数上使用 type 运算符以获得类似的效果。

当使用类型类而不是具体类型实例化泛型类型时,这会产生另一个更具体的类型类:

  1. seq[ref object] # 存储任意对象类型引用的序列
  2.  
  3. type T1 = auto
  4. proc foo(s: seq[T1], e: T1)
  5. # seq[T1]与 `seq` 相同,但T1允许当签名匹配时绑定到单个类型
  6.  
  7. Matrix[Ordinal] # 任何使用整数值的矩阵实例化

如前面的例子所示,在这样的实例化中,没有必要提供泛型类型的所有类型参数,因为任何缺失的参数都将被推断为具有 any 类型的等价物,因此它们将匹配任何类型而不受歧视。