第9章:模块系统
第 9 章:模块系统
9.1 模块基础
模块系统是管理大型 Guile 项目的关键机制,提供了命名空间隔离、接口封装和代码复用的能力。
9.1.1 为什么需要模块
没有模块的世界: 有模块的世界:
┌──────────────────┐ ┌──────────────────┐
│ (define x 1) │ │ (define-module │
│ (define y 2) │ │ (my-module) │
│ (define x 10) │ ← 冲突! │ #:export (x)) │
│ (define list 42) │ ← 覆盖! │ (define x 1) │
└──────────────────┘ └──────────────────┘
其他模块看不见内部定义
9.2 define-module
9.2.1 基本语法
;; 最简单的模块定义
(define-module (my-module))
;; 导出特定绑定
(define-module (my-module)
#:export (greet farewell))
(define (greet name)
(string-append "Hello, " name "!"))
(define (farewell name)
(string-append "Goodbye, " name "!"))
;; 内部辅助函数(不导出)
(define (internal-helper x)
(* x x))
;; 使用模块
;; (use-modules (my-module))
;; (greet "World") ; => "Hello, World!"
9.2.2 模块路径与命名
模块名称使用列表形式,对应文件系统的路径结构:
| 模块名 | 文件路径 |
|---|---|
(my-module) | my-module.scm |
(my utils) | my/utils.scm |
(app core helpers) | app/core/helpers.scm |
(lib (my module)) | lib/my/module.scm |
;; 文件: src/utils/math.scm
(define-module (utils math)
#:export (square cube factorial))
(define (square x) (* x x))
(define (cube x) (* x x x))
(define (factorial n)
(if (<= n 1) 1 (* n (factorial (- n 1)))))
9.2.3 模块声明选项
(define-module (my-module)
;; 导出
#:export (public-func public-var)
;; 选择性使用(替换绑定)
#:use-module (srfi srfi-1)
#:use-module ((srfi srfi-13) #:select (string-join string-split))
#:use-module ((srfi srfi-13) #:prefix s13-)
;; 替换现有绑定
#:replace (my-+)
;; 重导出
#:re-export (map filter)
;; 自动导入列表
#:autoload (ice-9 format) (format)
)
9.3 use-modules
9.3.1 基本导入
;; 简单导入
(use-modules (srfi srfi-1))
;; 现在可以使用 srfi-1 的所有导出函数
(filter even? '(1 2 3 4)) ; => (2 4 6)
;; 导入多个模块
(use-modules (srfi srfi-1)
(srfi srfi-13)
(ice-9 format))
;; 选择性导入(只导入需要的函数)
(use-modules ((srfi srfi-1) #:select (filter fold)))
;; 导入带前缀(避免冲突)
(use-modules ((srfi srfi-13) #:prefix s13/))
(s13/string-join '("a" "b" "c") "-") ; => "a-b-c"
;; 除了某些函数外全部导入
(use-modules ((srfi srfi-1) #:except (map)))
;; 使用 Guile 内置的 map,不用 srfi-1 的
9.3.2 导入对比
| 导入方式 | 语法 | 适用场景 |
|---|---|---|
| 全部导入 | (use-modules (mod)) | 不担心冲突时 |
| 选择性导入 | (use-modules ((mod) #:select (...))) | 只需少量函数 |
| 前缀导入 | (use-modules ((mod) #:prefix p/)) | 避免命名冲突 |
| 排除导入 | (use-modules ((mod) #:except (...))) | 有少量冲突 |
9.4 导出与接口
9.4.1 #:export 详解
(define-module (my-api)
#:export (
;; 导出函数
process-data
validate-input
;; 导出变量
*default-timeout*
*version*
;; 导出宏
with-config
;; 导出记录类型(构造函数和谓词)
make-user user?
user-name user-email
))
(define *version* "1.0.0")
(define *default-timeout* 30)
(define (process-data data)
;; 处理数据
data)
(define (validate-input input)
(and (string? input) (not (string-null? input))))
(define-syntax with-config
(syntax-rules ()
((_ config body ...)
(parameterize ((*current-config* config))
body ...))))
(define-record-type <user>
(make-user name email)
user?
(name user-name)
(email user-email))
9.4.2 re-export(重导出)
;; 重导出允许用户通过一个模块访问多个模块的功能
(define-module (my-utils)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-13)
#:use-module (ice-9 format)
;; 重导出 srfi-1 的常用函数
#:re-export (map filter fold append reverse)
;; 重导出 srfi-13 的常用函数
#:re-export (string-join string-split string-trim)
;; 重导出 format
#:re-export (format)
;; 自己的函数
#:export (greet))
;; 用户只需 (use-modules (my-utils)) 即可访问所有函数
9.5 模块内部机制
9.5.1 模块与变量
;; 每个模块都有自己的变量命名空间
;; 模块变量通过 (module-variable ...) 访问
;; 在 REPL 中查看模块
(module-map (lambda (sym var)
(format #t "~a: ~a~%" sym
(if (variable-bound? var)
(variable-ref var)
"*unbound*")))
(resolve-module '(guile-user)))
;; 访问其他模块的变量
(module-ref (resolve-module '(srfi srfi-1)) 'filter)
;; => #<procedure filter (pred list)>
;; 动态导入
(module-use! (current-module)
(resolve-interface '(srfi srfi-1)))
9.5.2 模块层次结构
scheme
├── (guile) ;; 核心模块
├── (guile-user) ;; REPL 默认模块
├── (srfi srfi-1) ;; 列表库
├── (srfi srfi-13) ;; 字符串库
├── (ice-9 format) ;; 格式化
├── (ice-9 regex) ;; 正则表达式
├── (ice-9 threads) ;; 线程
├── (ice-9 popen) ;; 进程管道
└── ...
9.6 实战项目结构
9.6.1 完整项目示例
my-project/
├── my-project/
│ ├── core.scm ;; (my-project core)
│ ├── utils.scm ;; (my-project utils)
│ ├── db/
│ │ ├── connection.scm ;; (my-project db connection)
│ │ └── query.scm ;; (my-project db query)
│ └── web/
│ ├── handler.scm ;; (my-project web handler)
│ └── middleware.scm ;; (my-project web middleware)
└── main.scm
;; my-project/core.scm
(define-module (my-project core)
#:use-module (srfi srfi-1)
#:use-module (ice-9 format)
#:export (app-version
log-info
log-error
with-error-handling))
(define *app-version* "0.1.0")
(define (app-version) *app-version*)
(define (log-info msg . args)
(apply format #t (string-append "[INFO] " msg "~%") args))
(define (log-error msg . args)
(apply format #t (string-append "[ERROR] " msg "~%") args))
(define-syntax with-error-handling
(syntax-rules ()
((_ body ...)
(catch #t
(lambda () body ...)
(lambda (key . args)
(log-error "~a: ~a" key args)
#f)))))
;; my-project/utils.scm
(define-module (my-project utils)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-13)
#:export (string-blank?
string-present?
alist-merge
filter-nil))
(define (string-blank? s)
(or (not s) (string-null? (string-trim-both s))))
(define (string-present? s)
(not (string-blank? s)))
(define (alist-merge . alists)
(fold (lambda (a acc)
(fold (lambda (pair acc)
(acons (car pair) (cdr pair) acc))
acc a))
'()
alists))
(define (filter-nil alist)
(filter (lambda (pair) (cdr pair)) alist))
;; main.scm
#!/usr/bin/env guile
!#
(add-to-load-path (dirname (current-filename)))
(use-modules (my-project core)
(my-project utils))
(define (main args)
(format #t "My Project v~a~%" (app-version))
(log-info "启动完成")
0)
(main (command-line))
9.7 公共 API 设计
9.7.1 接口封装原则
;; 良好的公共 API 设计
(define-module (my-lib)
;; 1. 明确导出列表
#:export (
;; 构造函数
make-config
;; 谓词
config?
;; 访问器
config-host
config-port
;; 核心功能
start-server
stop-server
))
;; 2. 内部实现不导出
(define (validate-config config)
;; 内部验证逻辑
...)
(define (internal-start config)
;; 内部启动逻辑
...)
;; 3. 公共函数做输入验证
(define (start-server config)
(unless (config? config)
(error "无效的配置" config))
(validate-config config)
(internal-start config))
9.7.2 版本兼容
;; 使用版本号管理 API 兼容性
(define-module (my-lib)
#:export (*lib-version*
make-thing
thing?
thing-value))
(define *lib-version* "2.1.0")
;; 弃用函数(保留兼容性,但发出警告)
(define (old-make-thing value)
(issue-deprecation-warning
"old-make-thing 已弃用,请使用 make-thing")
(make-thing value))
9.8 动态模块加载
;; 动态加载模块
(let ((mod (resolve-interface '(srfi srfi-1))))
(module-ref mod 'filter))
;; 条件加载
(define (try-load-module module-name)
(catch 'misc-error
(lambda ()
(let ((mod (resolve-interface module-name)))
(module-use! (current-module) mod)
#t))
(lambda (key . args)
(format #t "警告: 模块 ~a 不可用~%" module-name)
#f)))
(try-load-module '(srfi srfi-64)) ; 测试框架(可选)
9.9 本章小结
| 主题 | 要点 |
|---|---|
| define-module | 定义模块及其公共接口 |
| use-modules | 导入其他模块的功能 |
| #:export | 控制模块导出的绑定 |
| #:select/#:prefix | 选择性导入和前缀导入 |
| #:re-export | 重导出其他模块的函数 |
| 项目结构 | 模块名对应文件路径 |
扩展阅读
上一章:第 8 章:数据结构 下一章:第 10 章:输入输出