模块

Nim支持通过模块概念将程序拆分为多个部分。 每个模块都需要在自己的文件中,并且有自己的 命名空间 。 模块启用 信息隐藏 and 分开编译 。 模块可以通过 import 语句访问另一个模块的符号。 递归模块依赖 是允许的,但有点微妙。 仅导出标有星号(*)的顶级符号。 有效的模块名称只能是有效的Nim标识符(因此其文件名为 标识符.nim )。

编译模块的算法是:

  • 像往常一样编译整个模块,递归地执行import语句
  • 如果有一个只导入已解析的(即导出的)符号的环;如果出现未知标识符则中止

这可以通过一个例子来说明:

  1. # 模块A
  2. type
  3. T1* = int # 模块A导出类型 ``T1``
  4. import B # 编译器开始解析B
  5.  
  6. proc main() =
  7. var i = p(3) # 因为B在这里被完全解析了
  8.  
  9. main()
  1. # 模块 B
  2. import A # 这里没有解析A,仅导入已知的A符号。
  3.  
  4. proc p*(x: A.T1): A.T1 =
  5. # 这是有效的,因为编译器已经将T1添加到A的接口符号表中
  6. result = x + 1

Import语句

import 语句之后,可以跟随模块名称列表或单个模块名称后跟 except 列表以防止导入某些符号:

  1. import strutils except `%`, toUpperAscii
  2.  
  3. # 行不通:
  4. echo "$1" % "abc".toUpperAscii

没有检查 except 列表是否真的从模块中导出。 此功能允许针对不导出这些标识符的旧版本模块进行编译。

Include语句

include 语句与导入模块有着根本的不同:​​它只包含文件的内容。 include 语句对于将大模块拆分为多个文件很有用:

  1. include fileA, fileB, fileC

导入的模块名

可以通过 as 关键字引入模块别名:

  1. import strutils as su, sequtils as qu
  2.  
  3. echo su.format("$1", "lalelu")

然后无法访问原始模块名称。 符号 path/to/module"path/to/module" 可用于引用子目录中的模块:

  1. import lib/pure/os, "lib/pure/times"

请注意,模块名称仍然是 strutils 而不是 lib/pure/strutils 因此 无法 做:

  1. import lib/pure/strutils
  2. echo lib/pure/strutils.toUpperAscii("abc")

同样,以下内容没有意义,因为名称已经是 strutils

  1. import lib/pure/strutils as strutils

从目录中集体导入

语法 import dir / [moduleA, moduleB] 可用于从同一目录导入多个模块。

路径名在语法上是Nim标识符或字符串文字。如果路径名不是有效的Nim标识符,则它必须是字符串文字:

  1. import "gfx/3d/somemodule" # 在引号中因为'3d'不是有效的Nim标识符

伪import/include目录

目录也可以是所谓的“伪目录”。当存在多个具有相同路径的模块时,它们可用于避免歧义。

有两个伪目录:

  1. std: std 伪目录是Nim标准库的抽象位置。 例如,语法 import std / strutils 用于明确地引用标准库的 strutils 模块。

  2. pkg: pkg 伪目录用于明确引用Nimble包。 但是,对于超出本文档范围的技术细节,其语义为:使用搜索路径查找模块名称但忽略标准库位置 。 换句话说,它与 std 相反。

From import语句

from 语句之后,一个模块名称后面跟着一个 import 来列出一个人喜欢使用的符号而没有明确的完全限定:

  1. from strutils import `%`
  2.  
  3. echo "$1" % "abc"
  4. # 可能:完全限定:
  5. echo strutils.replace("abc", "a", "z")

如果想要导入模块但是想要对 module 中的每个符号进行完全限定访问,也可以使用 from module import nil

Export语句

export 语句可用于符号转发,因此客户端模块不需要导入模块的依赖项:

  1. # 模块B
  2. type MyObject* = object
  1. # 模块A
  2. import B
  3. export B.MyObject
  4.  
  5. proc `$`*(x: MyObject): string = "my object"
  1. # 模块C
  2. import A
  3.  
  4. # B.MyObject这里已经被隐式导入:
  5. var x: MyObject
  6. echo $x

当导出的符号是另一个模块时,将转发其所有定义。您可以使用 except 列表来排除某些符号。

请注意,导出时,只需指定模块名称:

  1. import foo/bar/baz
  2. export baz