模版 (template) 是一種較為高階的語法特性,透過程式碼代換來改寫程式,好處是可以跳脫型別的限制。模版有點類似於 C 的前置處理器 (preprocessor),Nim 編譯器會將使用到模版的程式碼代換掉,然後繼續編譯程式。Nim 的模版也是用 Nim 語言撰寫,程式設計者不需額外學習新的語法。
撰寫模版
見以下例子:
template `!=` (a, b: untyped): untyped =
not (a == b)
assert(5 != 6)
在這個例子中,5 != 6
會代換為 not (5 == 6)
。在 Nim 裡面,有些語法其實是用模板製作的。
Untyped vs. Typed
在撰寫模版時,可指定變數為 untyped
或 typed
,也可以指定為特定型別,型別的指定會有一些微妙的差別。以下的程式可以順利執行:
template declareInt(x: untyped) =
var x: int
declareInt(x) # valid
x = 3
但是以下的程式會引發錯誤:
template declareInt(x: typed) =
var x: int
declareInt(x) # invalid, because x has not been declared and so has no type
傳遞區塊
除了傳遞單一變數,模板也可以傳遞程式碼區塊,撰寫模板時,最後一個參數可視為程式碼區塊;在呼叫該模版時,加入 :
(引號) 即可。如下例:
template withFile(f, fn, mode, actions: untyped): untyped =
var f: File
if open(f, fn, mode):
try:
actions
finally:
close(f)
else:
quit("cannot open: " & fn)
withFile(txt, "ttempl3.txt", fmWrite):
txt.writeLine("line 1")
txt.writeLine("line 2")
Hygienic
Nim 的模板是衛生的 (hygienic),意指 Nim 的模板在代換的過程中不會將新的變數置入目前的模板中。
我們現在寫了一個模組 a:
# module a: a.nim
var
lastId = 0
template genId*: untyped =
inc(lastId)
lastId
我們引用模組 a 時,lastId
不會置入目前的模組中:
# main module: main.nim
import a
echo genId()
# Error
echo lastId
建立識別字
模板可以用反引號建立識別字。如下例:
template typedef(name: untyped, typ: typedesc) =
type
`T name`* {.inject.} = typ
`P name`* {.inject.} = ref `T name`
typedef(myint, int)
var x: PMyInt