在编程语言历史与思想的讨论中,Lisp 常被视为极具哲学意味的语言:代码即数据、数据即代码、极强的元编程能力以及活跃的 REPL 文化。近几年大语言模型(LLM, Large Language Model)像 ChatGPT、GPT 系列、Codex、以及各类基于 Transformer 的生成模型在编程辅助、代码生成与自然语言交互中表现出惊人的能力。有趣的问题是,LLM 是否可以被看作一种"可接受的 Lisp"?这个问题既有玩笑成分,也有值得认真深入的技术和哲学探讨价值。下面我们从若干维度剖析二者的相似处与本质差异,并讨论在实践中如何权衡与结合它们的优点。 先从"同构性"(homoiconicity)开始。Lisp 的同构性体现在源代码直接以数据结构(S-表达式)呈现,程序可以在运行时读入、生成、变换并再次评估这些表达式。
对 LLM 来说,输入与输出都是文本,文本可以同时承载代码与数据,因此表面上看也具有一种"同构"的味道:你可以把一段代码作为文本交给模型,再让模型生成新的代码文本。更进一步,借由提示(prompt)编写提示,甚至在链式提示中生成"写提示的提示",可以实现类似递归式的元编程流程。但是这种同构性是概率性的、表示层面的,而非结构化语法上的直接相等。Lisp 里 eval 的语义明确且可控,而 LLM 的"eval"更像是基于统计学习的预测,缺乏严格的语义等价保证。 元编程是另一个核心对照点。Lisp 的宏系统允许程序在编译或解释阶段以程序结构直接操纵语法树,生成新的代码,且宏具有可组合性和可抽象化的特性。
LLM 的元编程则以提示工程和链式生成为主:通过设计高质量提示可以让模型生成新的程序或生成器。提示写提示、自动化提示改进(prompt tuning)甚至少量学习(few-shot learning)都实现了元层次的能力。不同点在于 Lisp 的元编程是可验证和可重复的操作,一段宏在给定输入时返回确定性的输出(基于宏展开规则),而 LLM 的输出具有随机性,受温度、采样策略与模型权重影响。因此将 LLM 的提示技术等同于 Lisp 宏会掩盖概率性行为带来的不确定性和调试难度。 REPL(Read-Eval-Print Loop)文化是 Lisp 的灵魂之一:即时交互、快速试验、增量构建与调试让程序员能够把想法迅速落地。LLM 在某种程度上复制了互动式反馈回路的体验。
用户向模型询问、模型返回代码或解释、用户根据结果继续提问,这构成了类似 REPL 的互动循环。不同之处在于 Lisp 的 REPL 是确定性的评估环境,表达式的求值与副作用由语言规则控制;而 LLM 的"求值"更多依赖外部执行器(例如将生成的代码交给解释器、运行器或单元测试),模型本身不执行代码也不维护可复现的程序状态。模型可以模拟 REPL 的对话流,但缺少内部可追溯的执行语义,这使得调试和重现复杂交互成为挑战。 在错误处理与条件系统方面,Common Lisp 提供了强大的条件系统,可以在错误发生时建立可恢复的错误处理流程,让程序在错误点上下文中被中断、检查并决定如何继续。LLM 环境中没有内建的"条件处理"机制,但可以通过对话策略、提示模板与代理架构来近似实现:模型检测到问题后可以生成建议、回滚步骤或调用子代理进行修正。最近兴起的多代理系统、工具调用接口(tooling APIs)和链式调用(chain-of-thought)让模型能够模拟分层的错误恢复流程,但这些机制仍然依赖外部设计与管理,而不是语言内置的控制流与异常语义。
换言之,LLM 提供的是一种外部组合式的错误响应方式,而 Lisp 提供的是语言级、语法级的可恢复异常处理。 从符号处理与子符号(subsymbolic)表示的角度看,差异更为显著。Lisp 本质上操纵符号结构,程序员可以直接操作抽象语法树和符号;而 LLM 的内部表示是高维连续空间的向量与注意力权重,模型以概率分布的形式生成下一步标记。符号系统擅长精确推理和严格语义规则,子符号系统擅长从大量数据中学习模糊模式和语言习惯。将二者结合是近年来研究与工程努力的核心:用符号系统提供可解释的语义与逻辑约束,用神经模型提供模糊推理与生成能力。当需求是严格正确性与可证明性时,纯 LLM 很难替代传统符号化的编译器和解释器;当需求是快速原型、自然语言接口与模糊推断时,LLM 展现出无可比拟的效率。
可组合性和模块化是程序设计的重要属性。Lisp 的函数与宏天然支持高阶抽象和组合。LLM 在设计上鼓励把任务拆分为提示模板、工具接口和微服务的组合。LangChain、Agent 架构、函数调用接口等生态为"将模型输出与外部工具组合"提供了实践路径。通过把模型作为一个文本生成引擎并与可执行工具链相连,可以实现可复用的"模型为核心"的模块。但是可组合性的质量依赖于接口设计、工具契约和测试策略。
与 Lisp 那种内建语言级组合语义相比,LLM 组合更多依赖工程约束与运行时实践,组合的可靠性需要靠测试与监控来保证。 调试与可解释性是实际工程中的关键痛点。Lisp 程序可在 REPL 中逐步观察数据结构、调用栈、宏展开结果;调试器可以追踪函数调用并重构状态。LLM 的"黑箱"特性使得其内部为何产生某段代码、为何忽略某个约束变得不透明。研究者尝试通过注意力可视化、激活图谱、中间表示解码等方法还原模型决策过程,但与程序语义级别的可解释性相比仍有差距。工程上常见的做法是把 LLM 生成的代码作为草稿,通过单元测试、静态分析与代码审查来验证和修正,这形成了"生成 - 验证 - 修正"的闭环,某种程度上补偿了模型不可解释性的不足。
状态与副作用管理是另一条分水岭。Lisp 在表达副作用和状态变化方面提供了明确的语义,程序员可以控制可变状态、事务与回滚。LLM 本身没有内部可持久化的程序状态(尽管可以通过上下文窗口和外部存储模拟短期或长期记忆)。当需要跨多次交互保持一致状态时,工程实践往往借助外部数据库、内存缓存或记忆模块来维系上下文。LLM 与 Lisp 在状态管理上的差异意味着在需要严格状态一致性的应用中,单靠 LLM 难以替代具有内建状态语义的语言环境。 如何将二者优势结合以发挥最大效用?实践中有几条可行路径。
第一种是把 LLM 当作强大的代码生成助手与探索工具,用于生成初版实现、自动重构提示、提供对复杂 API 的自然语言抽象,但把关键逻辑与验证交给传统编译器、类型系统和测试套件。第二种是构建混合系统,在语言层面结合符号推理引擎与神经模型,例如用程序合成器约束 LLM 的输出格式、用静态类型检查器在生成后立刻验证并自动修正。第三种是设计以 LLM 为中心的 DSL(领域专用语言),在 DSL 层面定义严格的语义规则与接口,然后使用模型生成符合 DSL 语法的程序,借助编译器把 DSL 转换为可执行代码,从而把概率性生成限制在可验证的语法和契约之内。 从语言设计与未来研究方向来看,LLM 提供了对"自然语言即代码"理念的新一层思考。Lisp 之所以开创性地提供元编程与同构性,是因为语言本身把语法结构暴露给程序员。LLM 则通过大规模语料学习到语言中的常识与模式,为非程序员提供了低门槛的编程入口。
融合这两者意味着需要发展新的编程模式:带有自然语言接口的可验证语义层、面向提示的模块化库、自动化的生成 - 测试 - 修正流水线,以及支持多轮交互的状态管理机制。研究者和工程师还需要关注模型的不确定性,使其在关键路径上不会造成不可控的错误传播。 最终回答"LLM 是否是可接受的 Lisp"并不需要二元化的结论。LLM 在某些关键 Lisp 特性上展现出类似功能:它们能作为文本层面的同构体,支持层次化的提示写作,能在交互中模拟 REPL 的体验,并通过工程化手段接近条件处理与错误恢复的效果。然而其概率性、黑箱性、缺乏内建语义与状态管理使其无法完全等同于 Lisp 那样的语言生态。更现实的视角是把 LLM 视为一种新的编程范式组件,它与 Lisp 及其它传统语言互补:在创造性、快速原型与自然语言接口方面发挥优势,在需要形式化保证和可证明正确性的场景中交由符号系统处理。
对开发者和组织的建议很明确:不要寄希望于把 LLM 当作万能的解释器或替代现有编程语言。应把其定位为生产力工具和高层抽象生成器,把验证、执行和监控留给可靠的系统。与此同时,对语言学家和编程语言研究者来说,LLM 的出现提供了重新审视符号-子符号鸿沟的机会,推动混合系统、可验证生成与交互式编程模型的发展。未来可预见的是更多工具链把 LLM 与类型系统、符号验证和自动化测试无缝结合,形成既能快速表达也能严格验证的新一代编程体验。 回到最初的问题,称 LLM 为"可接受的 Lisp"在玩笑之余也提醒我们关注语言设计的核心价值:可组合的抽象、可控的元编程与可解释的语义。LLM 给了我们新的手段去实现某些 Lisp 风格的工作流,但要让它真正承担起 Lisp 那样的语言角色,还需要在语义明确性、可验证性与可组合性上做大量工程与理论工作。
把 LLM 当作强大的伙伴,而不是直接的替代者,会更有助于在实际产品与研究中取得稳健的进展。 。