关于 SBCL 的 require
对于如何加载 module,Common Lisp 标准里没有明确说明,而这几年普遍使用 ASDF 来解决依赖问题,很多 Common Lisp 实现也自带了 ASDF,并把 require 给“Hook”成了 ASDF 的实现,这里我以 SBCL 为例来分析。
require 的实现在 src/code/module.lisp 里,如下:
(defun require (module-name &optional pathnames) #!+sb-doc "Loads a module, unless it already has been loaded. PATHNAMES, if supplied, is a designator for a list of pathnames to be loaded if the module needs to be. If PATHNAMES is not supplied, functions from the list *MODULE-PROVIDER-FUNCTIONS* are called in order with MODULE-NAME as an argument, until one of them returns non-NIL. User code is responsible for calling PROVIDE to indicate a successful load of the module." (let ((name (string module-name))) (when (member name *requiring* :test #'string=) (require-error "~@" 'require module-name)) (let ((saved-modules (copy-list *modules*)) (*requiring* (cons name *requiring*))) (unless (member name *modules* :test #'string=) (cond (pathnames (unless (listp pathnames) (setf pathnames (list pathnames))) ;; ambiguity in standard: should we try all pathnames in the ;; list, or should we stop as soon as one of them calls PROVIDE? (dolist (ele pathnames t) (load ele))) (t (unless (some (lambda (p) (funcall p module-name)) *module-provider-functions*) (require-error "Don't know how to ~S ~A." 'require module-name))))) (set-difference *modules* saved-modules))))
require 参数接受一个 module 名字和一个可选的路径参数。先看看这段代码:
(let ((name (string module-name)))
将参数 module-name 转成字符串后本地绑定到 name 上,这意味着参数可以是一个 keyword 或者字符串:
(require "xx") (require :xx)
然后先判断模块是否已加载,如果模块没加载,才会去加载:
(unless (member name *modules* :test #'string=)
如果指定了 pathnames,将调用 load 来加载文件:
(dolist (ele pathnames t)
(load ele)))
如果没有指定路径,则调用 *module-provider-functions*
这个全局变量里的函数来加载:
(unless (some (lambda (p) (funcall p module-name)) *module-provider-functions*)
这里我直接在 REPL 里查看 *module-provider-functions*
里有哪些函数:
*module-provider-functions* ; => (ASDF/OPERATE:MODULE-PROVIDE-ASDF SB-IMPL::MODULE-PROVIDE-CONTRIB)
因为 require 里是用 some 函数来调用 *module-provider-functions*
列表里的函数,只要有一个调用成功就 OK 了。ASDF/OPERATE:MODULE-PROVIDE-ASDF 位于列表的第一个元素,所以默认会以 ASDF 来加载一个 module。