Common lisp REPL中的自动加载依赖文件
我是来自Clojure的Common Lisp新手,我已经习惯了这样的情况:Common lisp REPL中的自动加载依赖文件,common-lisp,slime,Common Lisp,Slime,我是来自Clojure的Common Lisp新手,我已经习惯了这样的情况: (ns x.core (:require [x.util :as u])) 当我启动REPL并计算该文件时,x.util会自动编译并在x.core中提供给我 在CommonLisp中,我正在尝试做类似的事情。在main.lisp中: (defpackage x.main (:use :x.util)) (in-package :x.main) (comment (load "util.lisp"))
(ns x.core
(:require [x.util :as u]))
当我启动REPL并计算该文件时,x.util
会自动编译并在x.core
中提供给我
在CommonLisp中,我正在尝试做类似的事情。在main.lisp中
:
(defpackage x.main
(:use :x.util))
(in-package :x.main)
(comment
(load "util.lisp"))
(defpackage x.util
(:use :common-lisp)
(:export :foo))
(in-package :cl-blog.util)
(defun foo () 3)
(defpackage #:x.main
(:use #:cl))
(in-package #:x.main)
(defun bar ()
(x.util:foo))
在util.lisp
中:
(defpackage x.main
(:use :x.util))
(in-package :x.main)
(comment
(load "util.lisp"))
(defpackage x.util
(:use :common-lisp)
(:export :foo))
(in-package :cl-blog.util)
(defun foo () 3)
(defpackage #:x.main
(:use #:cl))
(in-package #:x.main)
(defun bar ()
(x.util:foo))
我知道从main
中的util
访问foo
的唯一方法是评估comment
宏中的表单(我将其定义为类似于Clojure的注释,忽略其主体)
我也尝试过这个x.asd
文件:
(defsystem "x"
:version "0.1.0"
:author ""
:license ""
:components ((:module "src"
:components
((:file "util")
(:file "main" :depends-on ("util")))))
:description ""
:in-order-to ((test-op (test-op "x/tests"))))
但这似乎对我解决这个问题没有帮助
当我在REPL中编译
main.lisp
时,是否有一种更简单、更标准的方法来自动加载(或重新编译)util.lisp
?在REPL中处理多个文件的标准工作流是什么?手动这将是:
文件main.lisp
(eval-when (:load-toplevel :compile-toplevel :execute)
(load (merge-pathnames "utils.lisp" *load-pathname*)))
(defpackage x.main
(:use :x.util))
(in-package :x.main)
(foo)
文件util.lisp
(defpackage x.util
(:use :common-lisp)
(:export :foo))
(in-package :x.util)
(defun foo () 3)
(defpackage x.util
(:use :common-lisp)
(:export :foo))
(in-package :x.util)
(defun foo () 3)
然后调用(load)/my/path/main.lisp”)
如果需要,更复杂的东西将编译文件util.lisp(如果没有编译的文件,或者lisp文件会更新)。。。然后将加载编译后的代码
否则,您将定义一个包含两个文件的系统。
main.lisp
(eval-when (:load-toplevel :compile-toplevel :execute)
(load (merge-pathnames "utils.lisp" *load-pathname*)))
(defpackage x.main
(:use :x.util))
(in-package :x.main)
(foo)
(defpackage x.main
(:use :x.util))
(in-package :x.main)
文件util.lisp
(defpackage x.util
(:use :common-lisp)
(:export :foo))
(in-package :x.util)
(defun foo () 3)
(defpackage x.util
(:use :common-lisp)
(:export :foo))
(in-package :x.util)
(defun foo () 3)
然后定义一个ASDF系统“我的系统”(首先需要util.lisp,然后是main.lisp),加载该系统定义并调用(ASDF:LoadSystem“我的系统”)。。。然后,这将以指定的顺序/依赖项加载所有文件。只有当所需的命名空间位于类路径上的文件中,并且在文件和目录中的名称按照约定与命名空间相匹配时,才会显示Clojure。为了管理类路径(除其他外),可以使用deps、boot或leiningen之类的工具 在常见的Lisp语言中,这称为系统定义,它的工作方式有点不同 事实上,这方面的标准工具是ASDF(另一个系统定义工具)。在Clojure中,首先确定类路径,然后使用它立即启动整个应用程序,在Common Lisp中,首先启动映像,然后将系统加载到其中(有点类似于Clojure中的石榴)。系统在
.asd
文件中定义。ASDF知道在哪些标准位置查找此类文件,您可以通过配置文件甚至在运行时添加更多
重要的是要认识到,对于通用Lisp,系统和包是完全正交的概念。一个系统可以定义多个包,但一个包中的内容也可以在不同的系统中定义。此外,包与文件没有关系
因此,简而言之,您的x.asd
是绝对正确的。如果系统定义中有
:components ((:file "util")
(:file "main")))
那么您的util.lisp
可能是:
(defpackage #:x.util
(:use #:cl))
(in-package #:x.util)
(defun foo ()
'whatever)
和您的main.lisp
:
(defpackage x.main
(:use :x.util))
(in-package :x.main)
(comment
(load "util.lisp"))
(defpackage x.util
(:use :common-lisp)
(:export :foo))
(in-package :cl-blog.util)
(defun foo () 3)
(defpackage #:x.main
(:use #:cl))
(in-package #:x.main)
(defun bar ()
(x.util:foo))
要加载此系统,请在REPL中调用(asdf:load system“x”)
然后,无需执行任何操作即可启用对另一个包的引用。另一个包已经存在,因为ASDF加载了系统定义中声明为组件的所有文件
概括地说:不是先用一个完整的类路径定义启动一个映像,然后加载一个特定的文件,让Clojure先递归地加载依赖项,而是先启动一个基本映像,然后按照正确的依赖项顺序完全加载一个或多个已定义的系统
在common Lisp中,每个文件不定义一个包是很常见的。相反,一个包是在一个单独的文件中定义的,其他文件的顶部只有包中的
表单
foo.asd:
(defsystem "foo"
:serial t
:components ((:file "package")
(:file "utils)
(:file "foo")))
package.lisp:
(in-package #:cl-user)
(defpackage #:foo
(:use #:cl))
utils.lisp:
(in-package #:foo)
(defun frobnicate (bar)
#| … |#)
foo.lisp:
(in-package #:foo)
(defun handle-vie (r)
(wurble (frobnicate r)))
许多小型库只有一个包,而大型系统通常有更多的包,然后还使用某种伪层次结构(foo.bar、foo.baz)
ASDF中还有一个更新的附加项,称为包推断系统,它在某些方面与Clojure/Java机制更为相似,但就个人而言,我不认为这是一个普遍有用的东西。你说的“在REPL中编译main.lisp”是什么意思?你是调用编译文件
还是加载
(或两者兼而有之)?@RainerJoswig基于文档()我相信这两者都可以?理想情况下,我希望运行Slime的Slime编译和加载文件
(C-C-k
),并且不会因为x.util
尚未加载而失败。您可能真的描述了一个可复制的测试用例。注意,如果您定义一个包(名称空间)并使用另一个包,那么另一个包已经需要存在。因此,Lisp需要先查看另一个包的包定义,然后才能使用它。因此,通常的做法是加载所需的内容,然后创建一个新的包,可能使用现有的包。它不是一个模块,也不与文件等关联。文件由“系统”组织。系统是组成库或应用程序的一个或多个文件及其关系。通常在一个系统中,也有包定义——但如何构造它有很大的自由度。通常,我们会将包声明放入系统中的一个文件中,并确保该系统能够尽早加载它。我理解软件包和系统之间的区别;我特别遇到的是,当启动一个新的slimerepl时,我必须显式地加载各个依赖文件。我想找到一种自动完成的方法。谢谢你的回答。我还不清楚如何让SLIME执行必要的加载系统
ASDF魔术,但是如果我用*默认路径名默认值*
替换*加载路径名*
,则评估时
技巧有效,给出