型の定義
Nimの型定義についてです。
Nimには様々な型の定義方法があります。
型定義の種類として、
- enum
- object
- ref object
- ptr object
- 別名
- distinct
があります。
他にも型の特性を変えるものとして、not nil
があります。
定義方法
型定義の構文は、
type 型名 = 型定義の種類
です。
型定義の種類に上記のenumなどを記述します。
enum
enumは列挙型です。
NimのenumはC言語のenumと似ています。
type Direction = enum North East South West echo North
しかし、C言語と違うのはそれぞれに値を割り当てることができることです。
type Direction = enum North = "N" Eest = "E" South = "S" West = "W" echo North
{.pure.}
をつけることによって、どの列挙型に属すのかを記述するのを強制させることができます。
type Direction {.pure.} = enum North East South West echo North # error! echo Direction.North # works!
object
objectはいわゆる構造体で、デフォルトでスタック割当されます。
type GameObject = object x: int y: int var obj = GameObject(x: 1, y: 5) echo obj
ref object
ref objectはヒープ割当される構造体です。
type GameObject = ref object x: int y: int var obj = GameObject(x: 1, y: 5) echo obj.x, ":", obj.y
refとobjectは別のキーワードなので、既存の型のref版を作ることもできます。
type GameObject = object x: int y: int type PGameObject = ref GameObject var pobj = PGameObject(x: 1, y: 5) echo pobj.x, ":", pobj.y
object型とref型の両方で使える関数をor
を使うことで作ることもできます。
type GameObject = object x: int y: int type PGameObject = ref GameObject proc echoGameObject(obj: GameObject or PGameObject) = echo obj.x, ":", obj.y var obj = GameObject(x: 1, y: 5) var pobj = PGameObject(x: 2, y: 5) echoGameObject(obj) echoGameObject(pobj)
ptr object
ptr objectもヒープ割当される構造体ですが、GCによって追跡されません。よって通常はref objectを使うことが推奨されます。しかし、場合によっては低レベルまで制御して最適化することが求められるので、こういったものも用意されています。
type RawGameObject = ptr object x: int y: int var rawobj = cast[RawGameObject](alloc(sizeof(RawGameObject))) rawobj.x = 1 rawobj.y = 5 echo rawobj.x, ":", rawobj.y dealloc(rawobj)
Nimはシステムプログラミングを明確に意識しているので、こういった低レベル操作もできるようになっています。
ジェネリクス
type Buffer[T] = ref object data: seq[T] var buf = Buffer[int](data: @[1, 2, 3, 4, 5]) echo buf.data
NimのジェネリクスはC++のようにテンプレート系のジェネリクスです。そのためエラーメッセージが読みにくいという欠点があるので注意です。
別名
型に別名を付ける方法です。おそらくあまり使うことは無いと思いますが一応。
type IntSeq = seq[int] proc echoIntSeq(data: seq[int]) = echo data var a: IntSeq = @[1, 2, 3, 4, 5] var b: seq[int] = @[1, 2, 3, 4, 5] echoIntSeq(a) echoIntSeq(b)
あくまで別名を付けるだけなので、元の型と同じものとして扱うことができます。
distinct
distinctは既存の型から新しい型を作ります。なので、上記の別名とは違い、同じものとして扱うことはできません。
type Index = distinct int var a = 1 var b: Index = 2 # error! var b: Index = Index(2) # works!
not nil
not nilはその型がnilになることを防ぐ機能です。具体的には、objectが初期化されずに生成されることを防いだりするのに使います。
type GameObject = object x: int y: int type PGameObject = ref GameObject not nil var pobj: PGameObject # error! var pobjinit: PGameObject = PGameObject(x: 1, y: 5) # works!
まとめ
Nimの型は様々な定義方法があります。ゆるい型付けとしても扱えますし、堅めの型としても扱えます。それを柔軟と言うか一貫性が無いと言うかは人によると思いますが、面白い型の扱い方だと思います。