在 REPL 中自动加载依赖文件
Auto-load dependent files in REPL
我是 Common Lisp 的新手,来自 Clojure,并且习惯了这样的事情:
(ns x.core
(:require [x.util :as u]))
当我启动 REPL 并评估该文件时,x.util
会自动编译并在 x.core
中可供我使用。
在 Common Lisp 中,我正在尝试做类似的事情。在 main.lisp
:
(defpackage x.main
(:use :x.util))
(in-package :x.main)
(comment
(load "util.lisp"))
并在 util.lisp
中:
(defpackage x.util
(:use :common-lisp)
(:export :foo))
(in-package :cl-blog.util)
(defun foo () 3)
我知道从 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)
然后调用(load "/my/path/main.lisp")
.
如果需要,更复杂的东西会编译文件 util.lisp(如果没有编译文件或 lisp 文件会更新)...然后加载编译代码。
否则你会定义一个有两个文件的系统。
main.lisp
(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)
然后定义一个ASDF系统"my-system"(首先需要util.lisp然后main.lisp),加载该系统定义并调用(asdf:load-system "my-system" )... 然后这将加载指定 order/dependency.
中的所有文件
您为 Clojure 展示的内容仅在所需的命名空间位于类路径上的文件中时有效,在名称按照约定与命名空间匹配的文件和目录中。为了管理类路径(除其他事项外),您可以使用 deps、boot 或 leiningen 之类的东西。
在 Common Lisp 的说法中,这称为系统定义,它的工作方式有点不同。
事实上的标准工具是 ASDF(另一个系统定义工具)。在 Clojure 中,您首先确定类路径,然后使用它立即启动整个应用程序,而在 Common Lisp 中,您首先启动映像,然后将系统加载到其中(有点类似于 Clojure 中的石榴)。系统在 .asd
文件中定义。 ASDF 知道几个寻找此类文件的标准位置,您可以通过配置文件甚至在运行时添加更多。
重要的是要认识到系统和包对于 Common 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 #:cl))
(in-package #:x.main)
(defun bar ()
(x.util:foo))
为了加载这个系统,你在REPL中调用(asdf:load-system "x")
。
您无需执行任何操作即可启用对其他包的引用。另一个包已经在那里了,因为 ASDF 加载了系统定义中声明为 components
的所有文件。
概括一下:不是启动具有完整类路径定义的图像,然后加载特定文件以使 Clojure 首先递归地加载依赖项,而是启动一个基本图像,然后在一个文件中完全加载一个或多个定义的系统正确的依赖顺序。
在 Common Lisp 中,不为每个文件定义一个包是很常见的。相反,一个包是在一个单独的文件中定义的,然后其他文件的顶部只有 in-package
形式。
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 机制,但就我个人而言,我不相信这是一个普遍有用的东西。
我是 Common Lisp 的新手,来自 Clojure,并且习惯了这样的事情:
(ns x.core
(:require [x.util :as u]))
当我启动 REPL 并评估该文件时,x.util
会自动编译并在 x.core
中可供我使用。
在 Common Lisp 中,我正在尝试做类似的事情。在 main.lisp
:
(defpackage x.main
(:use :x.util))
(in-package :x.main)
(comment
(load "util.lisp"))
并在 util.lisp
中:
(defpackage x.util
(:use :common-lisp)
(:export :foo))
(in-package :cl-blog.util)
(defun foo () 3)
我知道从 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)
然后调用(load "/my/path/main.lisp")
.
如果需要,更复杂的东西会编译文件 util.lisp(如果没有编译文件或 lisp 文件会更新)...然后加载编译代码。
否则你会定义一个有两个文件的系统。
main.lisp
(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)
然后定义一个ASDF系统"my-system"(首先需要util.lisp然后main.lisp),加载该系统定义并调用(asdf:load-system "my-system" )... 然后这将加载指定 order/dependency.
中的所有文件您为 Clojure 展示的内容仅在所需的命名空间位于类路径上的文件中时有效,在名称按照约定与命名空间匹配的文件和目录中。为了管理类路径(除其他事项外),您可以使用 deps、boot 或 leiningen 之类的东西。
在 Common Lisp 的说法中,这称为系统定义,它的工作方式有点不同。
事实上的标准工具是 ASDF(另一个系统定义工具)。在 Clojure 中,您首先确定类路径,然后使用它立即启动整个应用程序,而在 Common Lisp 中,您首先启动映像,然后将系统加载到其中(有点类似于 Clojure 中的石榴)。系统在 .asd
文件中定义。 ASDF 知道几个寻找此类文件的标准位置,您可以通过配置文件甚至在运行时添加更多。
重要的是要认识到系统和包对于 Common 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 #:cl))
(in-package #:x.main)
(defun bar ()
(x.util:foo))
为了加载这个系统,你在REPL中调用(asdf:load-system "x")
。
您无需执行任何操作即可启用对其他包的引用。另一个包已经在那里了,因为 ASDF 加载了系统定义中声明为 components
的所有文件。
概括一下:不是启动具有完整类路径定义的图像,然后加载特定文件以使 Clojure 首先递归地加载依赖项,而是启动一个基本图像,然后在一个文件中完全加载一个或多个定义的系统正确的依赖顺序。
在 Common Lisp 中,不为每个文件定义一个包是很常见的。相反,一个包是在一个单独的文件中定义的,然后其他文件的顶部只有 in-package
形式。
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 机制,但就我个人而言,我不相信这是一个普遍有用的东西。