# 模块与导入 Deft 的模块系统基于文件路径。模块是按路径加载的 `.dft` 文件;顶层声明构成模块的公开接口(`def` 引入模块作用域的绑定)。`implements` 声明会在导入时添加模块级一致性检查。 ## `import` — 加载模块 `import "path" as alias` 加载模块并将其公开接口绑定到命名空间别名:
import "deft/stdext/async" as async

set results [async/await-all $futures]
导入路径有多种形式: - 相对路径 — `"./helpers"` / `"../lib/util"` 相对于导入文件所在目录解析。 - 限定包名 — `"owner/name/sub/path"`(例如 `deft/ui/components/button`)通过从 `:pkgs` 根目录自动注册的包挂载 解析。可在名称后追加 `@version` 进行固定(`deft/ui@0.1.0/...`); `@latest` 是未固定解析的显式别名。 - 挂载名 — 任何其他与预置文件 `:mounts` 条目或 `[pkg/mount ...]` 注册匹配的首段。最长挂载前缀优先,因此限定包名 不会被单段挂载遮蔽。 - 相对于当前工作目录 — 形如 `"foo/bar"` 的普通路径若其文件存在于 工作目录下,会先在本地解析,然后才查询挂载。 路径中省略 `.dft` 扩展名。限定包导入失败时会报告可用版本 (`no matching version; available: ...`);仅给出包前缀时会提示限定写法 (`did you mean "deft/ui/..."?`)。 ### 不导入直接解析 `[import/resolve "path"]` 在不加载的情况下回答“`import` 会加载哪个 文件?”—— 使用相同的解析器和优先级(相对路径、相对于当前工作目录、 带版本固定的挂载、stdext 回退):
[import/resolve "deft/ui/components/button"]
# => ".deft/packages/deft/ui/0.1.0/src/components/button.dft"
[import/resolve "deft/ui@0.1.0/components/button"]   # explicit pin
[import/resolve "./helpers"]                          # relative to caller
返回字面文件路径(包含 `loadFile` 会使用的 `.dft` 后缀 —— `.../markdown.syntax` 解析为 `.../markdown.syntax.dft`),若导入无法解析 则返回 nil。适用于工具:跳转到源码、文件监视、依赖图、诊断。 包的资源路径也可解析 —— 即 `import` 本身不会加载的目录和包根文件。 限定引用会先在该包代码目录下探测子路径是否为目录,然后在包根目录下 探测;单独的 `"owner/name"` 返回包根本身:
[import/resolve "deft/help/manual"]   # → "packages/help/manual" (directory)
[import/resolve "deft/help/help.md"]  # → "packages/help/help.md" (root file)
[import/resolve "deft/help"]          # → "packages/help" (package root)
因此,需要包数据目录(文档树、主题、资源)的应用可以从代码导入所用的 同一 `owner/name` 标识推导出该目录 —— 无需针对每种安装布局手写路径 探测。 ### `from ... import` `from "path" import @{ name1 name2 }` 将单个名称拉入当前作用域,无需 别名:
from "deft/stdext/async" import @{ await-all map-concurrently }

set xs [await-all $futures]
名称列表是 Deft 列表字面量 —— 多个名称用 `@{...}`,单个符号也可以。 ## 命名空间 每个运行时都有 `current-ns` —— 顶层 `def` 所进入的命名空间。默认 命名空间是 `"user"`。
[current-ns]                                  # => "user"
[in-ns "my-app"]                              # switch namespace
`in-ns` 在脚本中很少使用;它主要用于 REPL 会话以及动态管理命名空间的 运行时/工具代码。 ### 模块命名空间 当你 `import "foo/bar" as bar` 时,加载模块的命名空间可通过 `bar/name` 访问:
# In stdext/async.dft:
def await-all {|futures| ... }

# In your script:
import "deft/stdext/async" as async
[async/await-all $futures]                    # ← name resolved through alias
## `implements` — 模块符合性 模块可以声明它符合某个接口。在导入时,Deft 会验证接口要求的所有函数 都已导出:
# my-app/storage.dft
implements StorageInterface

def get {|key| ... }
def put {|key val| ... }
def delete {|key| ... }
如果缺少必需函数,导入会失败并报错,错误中会指出缺失的函数,且脚本 不会执行失败导入之后的代码: 没有 `implements` 声明的模块没有一致性要求。符合接口的模块可以导出 接口要求之外的额外函数。 > `implements` 是模块级一致性检查(该文件是否导出了所需名称?)。 > 它不同于 `defprotocol`/`defimpl`,后者是管理值上方法分派的类型级 > 协议。参见类型与协议。 ## 定义接口 接口是一个声明所需导出列表的模块。最简单的形式是记录预期名称的模块; Deft 的检查器会遍历导入并查找这些名称。
# stdext/storage_iface.dft
#
# Modules implementing StorageInterface must export: get, put, delete.

def required-names ^{:get :put :delete}
真正的接口模块通常还会定义实现者需要的类型或文档。规范示例参见 `test/test_impls.dft`。 ## 包 包是包含 `package.dft` 清单的 Deft 模块目录。包是分发单位:通过 `pkg/install` 安装,挂载到运行时,并从任意模块导入。
def manifest %Package{
    :owner "alice"
    :name "math-lib"
    :version "1.0.0"
    :description "Personal math helpers"
}
包清单格式、`pkg/*` 标准库函数以及 `.dfs` 分发文件参见包。 ## 搜索路径 未限定路径按以下顺序解析: 1. 相对于当前工作目录(`<cwd>/foo/bar.dft` 或其目录索引形式),仅当 文件存在时。 2. 挂载名(最长前缀优先):来自 `:pkgs` 自动扫描的限定包挂载 `owner/name[@version]`,然后是单段预置文件 `[pkg/mount]` 挂载。 3. 对 `syntax/*` 模块的 stdext 子路径回退。 `:pkgs` 根目录和包引用语法参见包。 ## 循环导入 Deft 不支持循环导入。如果模块 A 导入 B,而 B 又导入 A,加载器会在 加载时检测到循环并抛出错误。请重构以打破循环,或使用 `pub/*` 在运行时 于模块间通信。 ## 重新加载 `[reload "path"]` 重新加载磁盘上已更改的模块。在开发期间很有用(尤其 是在 REPL 或长时间运行的 TUI 应用中)。对旧定义的现有引用不会更新; 只有后续查找才会解析到新版本。
[reload "my-app/foo"]
## 已加载模块的信息 `info/*` 命名空间公开模块表:
[info/modules]                                # list of loaded module names
[info/defs]                                   # defs in the current namespace
[info/vars]                                   # vars in the current namespace
[info/resolve "name"]                         # resolve a name to its source
完整的 `info/*` API 参见元编程。 ## 快速参考 | 形式 | 用途 | |---|---| | `import "path" as alias` | 加载模块,绑定到别名 | | `from "path" import @{ names }` | 引入指定名称 | | `[in-ns "name"]` | 切换当前命名空间 | | `[current-ns]` | 获取当前命名空间名称 | | `implements InterfaceName` | 声明模块一致性(加载时检查) | | `[info/modules]` | 列出已加载模块 | | `[reload "path"]` | 重新加载模块 |