Nimのimplicit generics(暗黙のジェネリック)について
はじめに
こんにちは、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)
Discussion