关于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*里有哪些函数:

CL-USER> *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。