👑

Nimのimplicit generics(暗黙のジェネリック)について

2021/05/16に公開

はじめに

こんにちは、Neoと申します。
この記事では、Nimのimplicit generics(暗黙のジェネリック)という仕様について解説します。

Implicit genericsとは

抽象的な型(型クラス)を引数にとるプロシージャは、実際に呼び出すときには具象的な型が引数に渡されます。そのため、このようなプロシージャの引数は暗黙的に具象的な型にバインドされます。
これをImplicit generics(暗黙のジェネリック)といいます。

# すべてのtupeとobjectに一致する型クラス
type RecordType = tuple or object

proc echoFields(rec: RecordType) =
  for k, v in rec.fieldPairs:
    echo k, "=", v

このように、抽象型の引数をとるプロシージャは、暗黙のジェネリックであるとみなされます。

bind once

暗黙のジェネリックが適用されているプロシージャは、プログラム内で使用されるときに渡される一意な引数の型ごとに1回インスタンス化されます。
デフォルトでは、それぞれの型クラスは正確に1つの具象型にバインドされます。
このような型クラスをbind once型といいます。
systemモジュールの中のプロシージャの例です。

proc `==`(x, y: tuple): bool =
  ## `x`と`y`のタプルの型は同じである必要がある
  result = true
  for a, b in fields(x, y):
    if a != b: result = false

bind many

型クラスの前にdistinctキーワードをつけると、各型クラスをそれぞれ異なる型にバインドできます。
このような型クラスをbind many型といいます。

proc sample(a: Table, b: distinct Table) =
  ## `a`と`b`は異なる型になる
  ...

型パラメーターへの参照

暗黙のジェネリックを使ったプロシージャでは、多くの場合、その型クラスの型パラメーターを参照する必要があります。
型パラメーターには、.を用いて簡単にアクセスできます。

type Matrix[T, Rows, Columns] = object
  ...

proc `[]`(m: Matrix, row, col: int): Matrix.T =
  result = m.data[col * high(Matrix.Column) + row]

typedesc

引数として使われるtypedescには、暗黙のジェネリックが適用されます。

proc p(a: typedesc)

# 以下の定義と同じ

proc p[T](a: typedesc[T])

typedescは、bind many型です。

proc p(a, b: typedesc)

# 以下の定義と同じ

proc p[T, T2](a: typedesc[T], b: typedesc[T2])

typedesc型の引数は、それ自体を型として使うことができます。

proc p(a: typedesc; b: a) = discard

# 以下の定義と同じ

proc p[T](a: typedesc[T]; b: T) = discard

p(int, 4)
# 引数'a'は型、'b'は値である必要がある

暗黙のジェネリックの例

いくつかの例を紹介します。

proc p(t: Table; k: Table.Key): Table.Value

# 以下の定義と同じ

proc p[Key, Value](t: Table[Key, Value]; k: Key): Value
proc p(a: Table, b: Table)

# 以下の定義と同じ

proc p[Key, Value](a, b: Table[Key, Value])
proc p(a: Table, b: distinct Table)

# 以下の定義と同じ

proc p[Key, Value, KeyB, ValueB](a: Table[Key, Value], b: Table[KeyB, ValueB])

おわりに

暗黙のジェネリックについてあまり知らなかったので、記事にしてみました。
最後までお読みいただきありがとうございました!

参考

Nim Manual Implicit generics
Nimマニュアルを日本語訳してみた 暗黙のジェネリック(Implicit generics)

GitHubで編集を提案

Discussion