Common Lisp 包定义与 REPL 探索的依赖关系?

Common Lisp Package Definition with Dependencies for Exploration at the REPL?

这是对包和系统问题的另一种看法,特别是在个人项目中本地使用(可能类似于 2014 年的 HOWTO definition and usage of Common Lisp packages (libraries)?)。关于如何处理此问题的建议是什么?是否可以为描述 2022 年建议方法的来源提供链接? CL 的半衰期很长,这意味着很多旧的博客和书籍仍然有用,但不幸的是不是全部,新手很难知道哪些是仍然可行的指南。

这是我可能会为一个简单的个人探索项目编写的文件的开头。我的基本工作模式是启动 slime,在 emacs 中加载和编译它,然后切换到 slime repl 并使用 in-package 移动到我的包中并开始使用我的功能。

(ql:quickload "alexandria")
(ql:quickload "trivia")

(defpackage computing-with-cognitive-states
  (:use cl)
  (:nicknames :cwcs)
  (:local-nicknames (:alex :alexandria)))

(in-package :cwcs)

(defun sum-denom (dimension dist) ... ; and more defun's

前导的 ql:quickload 让我觉得丑陋,但没有它们我无法加载和编译。是否建议我先在 slime repl 中手动执行此操作?或者对于像这样的小用例,这是否被认为是可接受的做法?或者有其他推荐的方法吗?

如果我想有一天在别处使用我的包,是否建议我将它做成一个系统,或许使用 quickproject?或者当另一个会话(或另一个包定义)需要它时,更传统的方法只是 load 来自我系统上一个目录的 fasl 文件?

综上所述,题目是关于个人使用的代码的编写,可能是一个小文件,也可能是几个相互依赖的文件和一些外部系统。描述这些方法的一些合理方法和资源是什么?如果项目发展成为人们想要分享的东西,哪些是最容易扩大规模的?

简单的解决方案确实是使用 ASDF 定义一个系统。这确实是 quickproject 所做的全部,但即使对于较小的项目,它 (IMO) 也是一个完全合理的解决方案。

你通常会做什么,假设你把你的代码放在你系统上的一个有点标准的位置(你的文件系统上没有到处都有 lisp 项目!),是创建一个从 ~/quicklisp/local-projects/your/personal/projects/directory 的符号链接。这样,在这个存储库中使用 quickproject:make-project 创建项目(或手动编写 .asd 文件......)后,Quicklisp 将能够找到它,然后你可以 quickload 它如果您在 .asd 文件中指定了那些,则直接连同它的依赖项。

为了让事情更清楚:Quicklisp“简单地”是一个构建在 ASDF 之上的工具,它能够下载(然后加载)依赖项,以及它们的依赖项......等等,如果它们在.asd 文件位于它知道的地方。 “等效”可以是 Python 的 pip。另一方面,ASDF 指定了一种定义系统的方法,即一组源文件应该一起编译,以某种特定的顺序,使用一些依赖关系,等等。在较低的层次上,这更像是 C/C++ 的 makeCMake.

对于个人 one-off »脚本«,我的 .sbclrc:

中有一个小宏
(in-package #:cl-user)

(defmacro ql-require (&rest system-names)
  `(eval-when (:compile-toplevel :load-toplevel :execute)
     (ql:quickload ',system-names)))

那么我的脚本文件如下所示:

(in-package cl-user) ; naked symbols, I don't care much about package pollution here

(ql-require "alexandria" "arrows" …)

(defpackage my-script
  (:use cl alexandria arrows))

(in-package my-script)

;; script away!

我在 Emacs/SLIME、C-c C-k 中打开这些。这通常只有顶层的 »main«,所以它只会在 REPL 打印。在其他情况下,我之后点击 C-c ~ 从 REPL 开始工作。

一旦它不仅仅是一个脚本,而是我可能想要完全 quickload 的东西,我就为它创建一个简单的 .asd 文件,它只是将我依赖的系统从文件中的ql-require到系统定义中的:dependencies。包定义可以保留在一个 .lisp 文件中。我常用的lisp项目都在~/common-lisp/下,默认在ASDF加载路径下。一个非常简单的 .asd 文件可以如下所示:

(in-package #:asdf-user) ; uninterned symbols as string designators avoid package pollution

(defsystem "my-system"   ; system names are lower-case strings
  :dependencies ("alexandria" "arrows" …)
  :serial t
  :components ((:file "my-file")))

系统定义的文件名应与系统名称相同(此处为my-system.asd)。如果将多个系统放入一个文件中,它们的名称应该共享文件名作为前缀,可以选择后跟斜杠和一些限定后缀(例如 "my-system""my-system/test")。这确保了 ASDF 可以快速找到要加载的正确文件,而无需先加载它。

一旦我将功能拆分到几个文件中,我通常也会将 defpackage 放入其自己的文件中。

一旦我创建了多个包,我通常会为每个包创建子目录(:modules in :components in the system definition)。 (ASDF 可以选择制作 so-called 包推断系统 ,但我更喜欢中央系统定义。)

如果你想共享一个系统,你可能想要在系统定义中添加一个 :author 和一个 :license(即使它只是 CC0/Public 域)。如果您认为它具有普遍意义,您可以将它提交给 Quicklisp。