Common lisp REPL中的自动加载依赖文件

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"))

我是来自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"))
(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魔术,但是如果我用
*默认路径名默认值*
替换
*加载路径名*
,则
评估时
技巧有效,给出