模块
nim支持将一个程序分成若干个模块的概念。每个模块都有自己的文件。模块可以使信息隐藏和分开编译。一个模块可以通过import语句访问其他模块中的符号。只有被星号标记的顶层符号被导出。
# Module A
var
x*, y: int
proc `*` *(a, b: seq[int]): seq[int] =
# allocate a new sequence:
newSeq(result, len(a))
# multiply two int sequences:
for i in 0..len(a)-1: result[i] = a[i] * b[i]
when isMainModule:
# test the new ``*`` operator for sequences:
assert(@[1, 2, 3] * @[1, 2, 3] == @[1, 4, 9])
上面的模块出口是x和*,不是y。
一个模块的顶层语句在程序开始时执行。例如这可以用来初始化复杂的数据结构。
每个模块都有一个特殊的魔法常数isMainModule,如果这个模块作为主文件被编译,那么这个常数就为真。正如上面的例子所展示的这非常有用对于测试模块内嵌入。
相互依存的模块是可能的,但强烈反对,因为这样没有其他模块时一个模块不能重复使用。
编译模块的算法:
像往常一样编译整个模块,在import语句时递归。
如果这是一个循环只导入已经解析的符号(那是出口);如果一个未知的标识符出现,然后终止。
通过一个例子这是最好的说明:
# Module A
type
T1* = int # Module A exports the type ``T1``
import B # the compiler starts parsing B
proc main() =
var i = p(3) # works because B has been parsed completely here
main() # Module B
import A # A is not parsed here! Only the already known symbols
# of A are imported. A在这里不解析!只导入A已知的符号
proc p*(x: A.T1): A.T1 =
# this works because the compiler has already
# added T1 to A's interface symbol table
result = x + 1
一个模块的符号要符合module.symbol的语法。即使一个符号是模糊的,它也必须符合modual.symbol语法。一个符号是模糊的如果它被定在了两个不同的模块中(或者更多)并且它们都被第三个模块导入。
# Module A
var x*: string
# Module B
var x*: int
# Module C
import A, B
write(stdout, x) # error: x is ambiguous
write(stdout, A.x) # no error: qualifier used
var x = 4
write(stdout, x) # not ambiguous: uses the module C's x
但是这些规则并不适用于过程或者迭代器。这里应用重载规则:
# Module A
proc x*(a: int): string = $a
# Module B
proc x*(a: string): string = $a
# Module C
import A, B
write(stdout, x(3)) # no error: A.x is called
write(stdout, x("")) # no error: B.x is called
proc x*(a: int): string = nil
write(stdout, x(3)) # ambiguous: which `x` is to call?
不包括符号
正常情况下import语句会带来所有的输出符号。这可以被限制通过命名符号,它应该使用except修饰语被排除用。
import mymodule except y
From statement
我们已经看到简单的impoer语句只是导入所有的输出符号。另一种可选择的方法是只导入列出的符号是from import语句:
from mymodule import x, y, z
from语句也可以在符号上强行命名空间限定,从而使符号可用,但需要有资格被使用。
from mymodule import x, y, z
x() # use x without any qualification 使用a没有任何限制
from mymodule import nil
mymodule.x() # 必须用模块的名称作为前缀修饰x
x() #在这里没有任何资格使用x是一个编译错误
由于模块的名称一般都是很长的描述,你也可以定义一个较短的别名使用当使用限定符号的时候。
from mymodule as m import nil
m.x() # m is aliasing mymodule m是mymodule的别名
Include statement
include语句与导入一个模块有一些根本上的不同:include仅仅包含一个文件的内容,include语句将一个大的模块分割成几个文件是有用的。
include fileA, fileB, fileC