构建大型 Lisp 应用程序
Structuring large Lisp applications
我目前正在努力研究软件包、系统和公司。
我现在已经读了 Packages, systems, modules, libraries - WTF? 几遍了,我想我仍然很难把它写对。
如果我只是想将一个 Lisp 源文件分成两个文件,其中一个 "use" 另一个 - 我该怎么做?我需要为此建立一个系统吗?我应该使用模块吗? ……?我来自 Node.js 背景,你可以简单地说
var foo = require('./foo');
获取对文件 foo.js
中导出内容的引用。在 Lisp 中与此最接近的等价物是什么?
我理解 ASDF is for systems, and that it is bundled as part of Quicklisp,至少根据其文档:
ASDF comes bundled with all recent releases of active Common Lisp implementations as well as with quicklisp […]
好吧,Quicklisp 是为图书馆服务的,但它们之间的关系是什么? Quicklisp 是其他语言中的 "package manager" 之类的东西吗?如果是这样,那么 ASDF 究竟提供了什么?
抱歉问了这么多问题,但我认为这只是说明我在理解如何构建 Lisp 应用程序方面遇到了麻烦。任何帮助将不胜感激:-)
Common Lisp 中有一个 require
函数,但已弃用。如果您只是想将代码分成一个或多个部分,以便在 REPL 中以交互方式使用它,您可以将代码放在不同的文件中,然后 load
每个文件。相反,如果您想编写一个完整的 lisp 项目,我发现 quickproject 包非常有用,它为创建新包提供了一个简单的起点。
系统
要构建大型系统,请使用系统管理工具。一个'free'一个是ASDF.
您需要一个系统声明,其中列出了您的库或应用程序的各个部分。通常它会进入自己的文件。然后你加载一个系统或者编译一个系统。应该有教程如何做到这一点。
一个简单的 Lisp 系统可能有以下文件:
- 一个系统文件,描述系统、它的部件和任何其他需要的东西(其他系统)
- 描述所用命名空间的包文件
- 一个基本工具文件(用于宏使用的示例函数)
- 一个列出宏的宏文件(用于使它们在软件的其余部分之前得到 compiled/loaded)
- 一个或多个具有功能的其他文件。
Quicklisp 独立于此。这是一个软件分发工具。
快速编译和加载文件
但您也可以在没有 系统 工具的情况下以老式方式编译和加载文件:
(defparameter *files*
'("/yourdir/foo.lisp" "/yourdir/bar.lisp"))
(defun compile-foobar ()
(mapc #'compile-file *files*))
(defun load-foobar ()
(mapc #'load *files*))
(defun compile-and-load ()
(mapc (lambda (file)
(load (compile-file file)))
*files*))
实际上可能还有更多内容,但通常就足够了。编写自己的构建工具应该很容易。典型的 system 工具将提供更多功能,以结构化方式构建更复杂的软件。这些工具的许多想法至少可以追溯到 35 年前。例如参见 [=12=].
文件的作用
请注意,在普通的 Common Lisp 中,文件的作用及其语义并不是很复杂。
一个文件不是一个命名空间,它不是一个class/subclass或一个对象,它是不是一个模块。你可以随意在一个文件中混合 Lisp 结构。文件可以是任意大的(例如,一个复杂的库有一个版本,它作为 30000 行的单个源文件交付)。在标准语义中,文件发挥作用的唯一真实位置是在编译文件时。编译文件有什么副作用?编译器可以做什么优化?
除此之外,假设开发环境提供加载和编译文件组等服务,又名 系统,提供编译错误的概述,记录定义的源位置,可以查找定义等。像 ASDF 这样的工具处理 system 部分。
我目前正在努力研究软件包、系统和公司。
我现在已经读了 Packages, systems, modules, libraries - WTF? 几遍了,我想我仍然很难把它写对。
如果我只是想将一个 Lisp 源文件分成两个文件,其中一个 "use" 另一个 - 我该怎么做?我需要为此建立一个系统吗?我应该使用模块吗? ……?我来自 Node.js 背景,你可以简单地说
var foo = require('./foo');
获取对文件 foo.js
中导出内容的引用。在 Lisp 中与此最接近的等价物是什么?
我理解 ASDF is for systems, and that it is bundled as part of Quicklisp,至少根据其文档:
ASDF comes bundled with all recent releases of active Common Lisp implementations as well as with quicklisp […]
好吧,Quicklisp 是为图书馆服务的,但它们之间的关系是什么? Quicklisp 是其他语言中的 "package manager" 之类的东西吗?如果是这样,那么 ASDF 究竟提供了什么?
抱歉问了这么多问题,但我认为这只是说明我在理解如何构建 Lisp 应用程序方面遇到了麻烦。任何帮助将不胜感激:-)
Common Lisp 中有一个 require
函数,但已弃用。如果您只是想将代码分成一个或多个部分,以便在 REPL 中以交互方式使用它,您可以将代码放在不同的文件中,然后 load
每个文件。相反,如果您想编写一个完整的 lisp 项目,我发现 quickproject 包非常有用,它为创建新包提供了一个简单的起点。
系统
要构建大型系统,请使用系统管理工具。一个'free'一个是ASDF.
您需要一个系统声明,其中列出了您的库或应用程序的各个部分。通常它会进入自己的文件。然后你加载一个系统或者编译一个系统。应该有教程如何做到这一点。
一个简单的 Lisp 系统可能有以下文件:
- 一个系统文件,描述系统、它的部件和任何其他需要的东西(其他系统)
- 描述所用命名空间的包文件
- 一个基本工具文件(用于宏使用的示例函数)
- 一个列出宏的宏文件(用于使它们在软件的其余部分之前得到 compiled/loaded)
- 一个或多个具有功能的其他文件。
Quicklisp 独立于此。这是一个软件分发工具。
快速编译和加载文件
但您也可以在没有 系统 工具的情况下以老式方式编译和加载文件:
(defparameter *files*
'("/yourdir/foo.lisp" "/yourdir/bar.lisp"))
(defun compile-foobar ()
(mapc #'compile-file *files*))
(defun load-foobar ()
(mapc #'load *files*))
(defun compile-and-load ()
(mapc (lambda (file)
(load (compile-file file)))
*files*))
实际上可能还有更多内容,但通常就足够了。编写自己的构建工具应该很容易。典型的 system 工具将提供更多功能,以结构化方式构建更复杂的软件。这些工具的许多想法至少可以追溯到 35 年前。例如参见 [=12=].
文件的作用
请注意,在普通的 Common Lisp 中,文件的作用及其语义并不是很复杂。
一个文件不是一个命名空间,它不是一个class/subclass或一个对象,它是不是一个模块。你可以随意在一个文件中混合 Lisp 结构。文件可以是任意大的(例如,一个复杂的库有一个版本,它作为 30000 行的单个源文件交付)。在标准语义中,文件发挥作用的唯一真实位置是在编译文件时。编译文件有什么副作用?编译器可以做什么优化?
除此之外,假设开发环境提供加载和编译文件组等服务,又名 系统,提供编译错误的概述,记录定义的源位置,可以查找定义等。像 ASDF 这样的工具处理 system 部分。