隐式泛型

一个类型类可以直接作为参数的类型使用。

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

以这种方式使用类型类的过程,被当成是 implicitly generic “隐式泛型”。 在程序中,对于每个特定参数类型组合时被实例化一次。

通常,重载解析期间,每一个被命名的类型类都将被绑定到单一的具体类型。我们称这些类型类为 bind once “单一绑定” 类型。以下是从 system 模块里直接拿来的例子:

  1. proc `==`*(x, y: tuple): bool =
  2. ## 需要 `x` 和 `y` 都是相同的元组类型
  3. ## 针对元组的泛型运算符 `==` 建立于 `x` 和 `y` 各字段的相等性之上
  4. result = true
  5. for a, b in fields(x, y):
  6. if a != b: result = false

另一种情况是用 distinct 修饰类型类,这将允许每一参数绑定到匹配类型类的不同类型。这样的类型类被称为 bind many “多绑定” 类型。

使用了隐式泛型的过程,常常需要引用匹配的泛型类型内的类型参数。使用 . 语法能便捷地实现此功能:

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

下面是关于隐式泛型更多的例子:

  1. proc p(t: Table; k: Table.Key): Table.Value
  2. # 大致等同于:
  3. proc p[Key, Value](t: Table[Key, Value]; k: Key): Value
  1. proc p(a: Table, b: Table)
  2. # 大致等同于:
  3. proc p[Key, Value](a, b: Table[Key, Value])
  1. proc p(a: Table, b: distinct Table)
  2. # 大致等同于:
  3. proc p[Key, Value, KeyB, ValueB](a: Table[Key, Value], b: Table[KeyB, ValueB])

typedesc 作为参数类型使用时,也会产生隐式泛型,typedesc 有其独有的规则:

  1. proc p(a: typedesc)
  2. # 等同于以下写法:
  3. proc p[T](a: typedesc[T])

typedesc 是一个 “多绑定” 类型类:

  1. proc p(a, b: typedesc)
  2. # 大致等同于:
  3. proc p[T, T2](a: typedesc[T], b: typedesc[T2])

typedesc 类型的参数本身可以作为一个类型使用。如果其作为类型使用,就是底层类型。换言来说,”typedesc” 剥离了一层。

  1. proc p(a: typedesc; b: a) = discard
  2. # 大致等同于:
  3. proc p[T](a: typedesc[T]; b: T) = discard
  4. # 所以这是合法的:
  5. p(int, 4)
  6. # 这里参数 'a' 需要的是一个类型, 而 'b' 需要的则是一个值。