Common Lisp

来自 ArchWiki

Common Lisp 是一种高度动态、多范式语言,强调交互性和性能。由于完全标准化,有多个独立的实现可供选择。

安装

sbcl (http://www.sbcl.org/) 是最流行的 FOSS 实现,并且通常在整个生态系统中具有最高的兼容性。它的编译器生成原生机器代码,并且该项目每月发布版本。

它在 Lisp 中于 ~/.sbclrc 内配置,但您可能不需要这个,具体取决于您选择的依赖管理策略。

其他实现

还有许多其他可用的实现。除了以下选项外,还有两个主要的商业 Lisp 实现,Allegro 和 LispWorks,但它们具有严格的许可条款,并且未在 Arch Linux 生态系统中打包。

活跃

这些实现与现代工具无缝协作,可用于严肃的项目。

  • ABCL — Armed Bear Common Lisp:在 Java 虚拟机上运行。
https://common-lisp.net/project/armedbear/ || abclAUR
  • CCL — Clozure Common Lisp:基于 Open Macintosh Common Lisp,以编译速度快而闻名。
https://ccl.clozure.com/ || cclAUR
  • Clasp — Clasp:一种较新的 Common Lisp 实现,可与 C++ 库互操作,并使用 LLVM 进行编译为原生代码。
https://clasp-developers.github.io/ || clasp-clAUR
  • ECL — Embeddable Common Lisp:编译为 C 代码,从而提供良好的 C 集成和可嵌入性。
https://common-lisp.net/project/ecl/ || ecl

历史

虽然这些实现有软件包可用,但库和工具的兼容性有时值得怀疑,并且某些实现不再积极维护。

  • CLISP — ANSI Common Lisp 解释器、编译器和调试器:提供良好的 C 集成和可嵌入性。
https://gnu.ac.cn/software/clisp/ || clisp
  • CMUCL — CMU Common Lisp:最初在卡内基梅隆大学开发的仅 POSIX 实现。
https://gitlab.common-lisp.net/cmucl/cmucl/-/wikis/home || cmucl
  • GCL — GNU Common Lisp:Kyoto Common Lisp 在 80 年代的后代,也是 ECL 的同级。
https://gnu.ac.cn/software/gcl/ || gclAUR
  • MKCL — Mankai Common Lisp:Kyoto Common Lisp 的类似后代。
https://mkcl.common-lisp.dev/ || mkcl-gitAUR

依赖管理

Common Lisp 中的依赖管理传统上涉及 Quicklisp,但在最近几年,现代替代方案已经出现。

Vend

Vend 是一个简单的工具,用于直接在项目仓库中下载和访问依赖项。它不需要任何特殊配置。

安装 vendAUR 包后,您可以从项目的顶层目录运行 vend get

[vend] Downloading dependencies.
[vend] Fetching FN-MACRO
[vend] Fetching ARROW-MACROS
[vend] Fetching TRANSDUCERS
...
[vend] Done.

之后,vend repl 将启动 REPL 会话。

编辑器集成

Emacs

对于 Sly/Slime 的 Emacs 用户,可以通过以下方式配置编辑器内 REPL

(setq sly-default-lisp 'sbcl
      sly-lisp-implementations '((sbcl ("vend" "repl" "sbcl")  :coding-system utf-8-unix)
                                 (ecl  ("vend" "repl" "ecl")   :coding-system utf-8-unix)))

您可以自由地以类似的方式添加其他编译器。根据 Slime 的需要进行调整。

Lem

Lem 用户可以使用以下命令打开 REPL

C-u M-x slime <RET> vend repl
Vim

与支持多个正在运行的 Lisp 的 Emacs 不同,Slimv 需要一个独立服务器,该服务器在 Vim 重启后仍然存在。

如果我们希望 Slimv 可以看到 vendored/ 中的依赖项,我们必须从项目目录手动启动其服务器

> cd project/
> vend repl ecl --load /home/YOU/.vim/pack/common-lisp/start/slimv/slime/start-swank.lisp

现在,Vim 中的 ,c (REPL 连接) 将自动找到正在运行的服务器,您可以加载项目和 vendored/ 中可用的任何系统。

如果要切换项目,您需要手动退出 REPL 服务器并如上所述重新启动它。您可能还希望为上面显示的长调用设置 shell 别名或创建包装脚本。

OCICL

OCICL 是 Quicklisp 的现代替代品,它将软件包作为来自中央软件包仓库的 OCI 兼容工件 分发。与 Quicklisp 类似,它必须由用户配置以接管和管理依赖项加载。

安装 ociclAUR 后,运行 ocicl setup,这会将以下内容添加到您的编译器配置文件中

#-ocicl
(when (probe-file #P"/home/green/.local/share/ocicl/ocicl-runtime.lisp")
  (load #P"/home/green/.local/share/ocicl/ocicl-runtime.lisp"))
(asdf:initialize-source-registry
  (list :source-registry (list :directory (uiop:getcwd)) :inherit-configuration))

现在,在 REPL 中,任何尝试在未知依赖项上运行 (asdf:load-system :foo) 的操作都将自动下载它。进一步的项目配置是通过 systems.csv 文件完成的,您需要将该文件提交到项目仓库。有关更多设置和使用详情,请参阅 README

Quicklisp

Quicklisp 是将库获取和加载到 Common Lisp 程序中的原始方法。默认情况下,它有一个由机器上所有程序共享的单个全局包缓存,并且它从一个保守更新的仓库(也可能令人困惑地称为 Quicklisp)中拉取包。

安装 quicklisp 包后,可以按如下方式为特定编译器注册它

> sbcl --load /usr/share/quicklisp/quicklisp.lisp
* (quicklisp-quickstart:install)
* (ql:add-to-init-file)

之后,可以在所有未来的 REPL 会话中使用 (ql:quickload "foo") 来加载依赖项,并在必要时下载它。

要更新通过 Quicklisp 安装的所有软件包,请在 REPL 中运行以下命令

(ql:update-all-dists)
注意: 一般来说,Quicklisp 假设您创建的所有 Common Lisp 项目都位于 ~/common-lisp/ 下。这对于 Vend 和 OCICL 等其他方法并非如此。

Ultralisp

Ultralisp 是一个备用的 Quicklisp 仓库,它提供所有软件包的滚动发布版本,通常与 Github 等上可用的版本同步更新。

要注册它,请在 REPL 中运行以下命令

(ql-dist:install-dist "http://dist.ultralisp.org/" :prompt nil)

然后,如果您通过 ql:quickload 加载的软件包在 Ultralisp 中更新,它将从那里加载而不是从 Quicklisp 加载。

Qlot

或者,如果您对 Quicklisp 没问题,但想要项目本地依赖项,则可以使用 Qlot (qlotAUR)。

安装完成后,可以通过以下方式初始化项目仓库以使用它

qlot init

自定义依赖

使用 Qlot,所有自定义依赖项位置都在 qlfile 中声明。例如,要声明 Ultralisp 的使用,只需添加

dist http://dist.ultralisp.org

或者指定对本地文件系统上依赖项的依赖

local foobar /home/you/code/common-lisp/foobar

有关更多选项,请参阅 Qlot 的 README。

要安装声明的依赖项,请运行

qlot install

调用 REPL

要加载具有当前 Qlot 环境的 REPL,请运行

qlot exec sbcl

对于 Sly/Slime 的 Emacs 用户,请考虑如何通过以下方式启动编辑器内 REPL

(setq sly-default-lisp 'qlot-sbcl
      sly-lisp-implementations '((qlot-sbcl ("qlot" "exec" "sbcl") :coding-system utf-8-unix)))

根据 Slime 的需要进行调整。

开发环境

Emacs

Common Lisp 开发通常在 Emacs 中完成,通过 slime 或较新的 sly。两者都在社区中被广泛采用,并具有类似的使用界面。

Portacle

Portacle 是一个基于 Emacs 的一体化 Common Lisp 开发环境。其目的是让初学者轻松入门 Lisp。

Lem

Lem (lem-editor-gitAUR) 是一款较新的编辑器,风格类似于 Emacs,但完全用 Common Lisp 编写和配置。它具有终端和 GUI 前端,并支持多种语言。

Vim

Slimv 是从 Emacs 移植的 Slime 版本,它利用 Slime 的 Swank 后端服务器,提供与 Emacs 非常相似的体验。要手动安装插件

mkdir -p ~/.vim/pack/common-lisp/start
cd ~/.vim/pack/common-lisp/start
git clone --depth 1 https://github.com/kovisoft/slimv.git

重启 Vim 并打开一个 Common Lisp 文件。Slimv 应该处于活动状态,,c 将启动 REPL 服务器并将您连接到它。

VSCode

VSCode 对 Common Lisp 的支持在编辑器中最弱,但仍然是可能的。Alive 插件充当完整的开发环境,并带有自己的 LSP,这需要手动安装。

提示

管理 Init 文件

库作者经常使用多个编译器实现来测试他们的代码。但是,每个实现都有自己唯一命名的 init 文件,例如 .sbclrc.eclrc,但这些文件的内容通常是相同的。与其为每个编译器手写这些文件,不如创建一个主文件并将其他文件符号链接到它。例如,如果您认为 .sbclrc 是您的“主配置文件”,那么

ln -s /home/you/.sbclrc .eclrc
ln -s /home/you/.sbclrc .abclrc

等等。主要的 init 文件有

~/.sbclrc          # for SBCL
~/.abclrc          # for ABCL
~/.ccl-init.lisp   # for CCL
~/.clasprc         # for CLASP
~/.eclrc           # for ECL
~/.clisprc.lisp    # for CLISP
~/.cmucl-init.lisp # for CMUCL
~/.mkclrc          # for MKCL
~/.clinit.cl       # for Allegro

减少编译输出的冗余信息

特别是 SBCL 可能会非常热衷于输出大量编译器“notes”。要静默这些 notes,同时仍然显示通常的错误和警告,请在您的 ~/.sbclrc 中设置以下内容

(declaim (sb-ext:muffle-conditions sb-ext:compiler-note))

故障排除

Quicklisp 无法加载本地项目

(ql:quickload "...") 用于加载外部依赖项和本地项目。但是,如果您的本地项目未位于 ~/common-lisp/ 下,则它将无法加载(或者甚至可能从在线拉取,如果您已发布它)。

实际上,Quicklisp 只是在获取和组织软件包。在内部,它使用 ASDF 构建系统来实际加载它们。也可以加载任何本地项目或您已通过 (asdf:load-system "foo") 下载的软件包,默认情况下,ASDF 仅在 ~/common-lisp/ 中查找本地项目。

虽然可以配置,但一个更简单的方法是使用符号链接

ln -s /home/you/code/common-lisp/ common-lisp
注意: 如果使用 Vend、OCICL 或 Qlot,则上述符号链接不是必需的。只要您已正确设置它们,(asdf:load-package "foo") 就会“正常工作”。

什么是项目、系统和包?

本文的其余部分以“包”这个术语在其他编程语言中通常使用的方式来使用,与“库”同义。然而,尽管在 90 年代中期标准化,但 Common Lisp 的最早形式可以追溯到 80 年代,因此涉及项目管理的术语有所不同。本质上

  • 项目:一组系统。在其他语言中有时称为“工作区”。
  • 系统:一组包。这些可以代表库和“可执行文件”。
  • 包:一组函数和类型定义。在其他语言中通常称为“模块”,但可以跨越多个文件。

正如你所看到的,在其他地方通常被称为库(library)的东西在 Common Lisp 中被称为 “系统”(system)。因此得名 asdf:load-system点击这里查看 一个相互依赖的多系统项目的例子。

另请参阅