Skip to content

LISP - REPL

REPL 代表 Read(读取)-Eval(求值)-Print(打印) Loop(循环)。它是交互式 Lisp 开发体验的核心。与许多语言典型的编辑-编译-运行循环不同,REPL 允许你在应用程序运行时逐步构建、测试和调试它。这种紧密的反馈循环是 Lisp 最强大的特性之一。

  • 读取(Read):REPL 读取你输入的 Lisp 表达式(S-表达式)。
  • 求值(Eval):Lisp 系统对该表达式进行求值(执行)。
  • 打印(Print):求值结果打印到控制台。
  • 循环(Loop):REPL 循环回到开始,准备好接受下一个表达式。

这个简单的循环实现了动态且探索性的编程风格,被称为 REPL 驱动开发(REPL-Driven Development)。

虽然你可以使用裸命令行 REPL,但现代 Lisp 开发将其集成到强大的工具链中。以下是关键组件:

  • Lisp 实现(Lisp Implementations):这些是编译器和运行时系统。流行的选择包括以高性能著称的 SBCL (Steel Bank Common Lisp)、以快速编译速度和友好 REPL 著称的 Clozure CL (CCL),以及用于将 Lisp 嵌入 C 应用程序的 ECL (Embeddable Common Lisp)。
  • 开发环境(Development Environments):最流行的环境是 Emacs 搭配 SLIME (Superior Lisp Interaction Mode for Emacs) 或 Sly(SLIME 的现代分支)扩展。这些将 Emacs 变成一个功能齐全的 Lisp IDE,提供了与正在运行的 Lisp 进程的无缝连接。
  • 库管理器(Library Manager):Quicklisp 是 Common Lisp 事实上的包管理器。它允许你轻松查找、下载和加载数千个开源库。
  • 构建系统(Build System):ASDF (Another System Definition Facility) 是定义 Lisp 项目(一个“系统”)中文件如何相互关联以及它们应如何编译和加载的标准。

想象一下使用 Emacs 和 SLIME。你的工作流程如下所示:

  1. 你启动 SLIME (M-x slime),它会在后台启动一个 Lisp 实现(如 SBCL)并连接到它。
  2. 你有两个主要窗口:你的源代码文件(.lisp)和 SLIME REPL。
  3. 你在源文件中编写一个函数,例如 (defun hello (name) (format t "Hello, ~a!" name))。
  4. 将光标放在该函数中,你按下组合键 (C-c C-c)。该函数会立即编译并加载到正在运行的 Lisp 镜像中。没有针对整个项目的独立编译步骤。
  5. 你切换到 REPL 并立即测试它:(hello "World")。
  6. REPL 打印 Hello, World!。
  7. 你决定修改函数。你编辑源代码,使用 C-c C-c 重新编译该函数,然后在 REPL 中再次测试。应用程序状态得到保留。
  • 交互式调试(Interactive Debugging):当发生错误时,你不会得到堆栈跟踪和崩溃。你会被带入 REPL 内部 的交互式调试器,在那里你可以检查变量、修复代码,并从中断的地方继续执行。
  • 代码自省(Code Introspection):你可以向 Lisp 镜像查询信息。例如,查找函数的文档(describe),查找其源代码(M-.),或查看所有调用它的函数列表。
  • 对象检查(Object Inspection):你可以使用 Inspector 详细检查复杂对象,遍历其内部结构。
  • Tab 补全(Tab Completion):REPL 为函数名、变量、类名和文件路径提供上下文感知的补全。
  • 加载文件和系统(Loading Files and Systems):你可以将整个文件或项目(使用 ASDF 定义)直接加载到正在运行的环境中。
  • 自底向上构建(Build Bottom-Up):从编写和测试小型纯函数开始。将它们组合成更大的组件,并在 REPL 中逐步测试。
  • 保持 Lisp 镜像活跃(Keep Your Lisp Image Alive):除非绝对必要,否则不要重启 Lisp 进程。目标是在程序运行时对其进行演进。
  • 拥抱交互性(Embrace Interactivity):将调试器、检查器和其他工具作为理解和与代码交互的主要方式。
  • 保存你的工作(Save Your Work):在你进行交互式构建时,确保最终代码保存到你的 .lisp 源文件中。REPL 用于探索;文件用于持久化。