当我们谈论大语言模型在编程任务中的表现时,一个有趣的现象逐渐浮出水面:Lisp 这门拥有超过六十年历史的语言,对现代 LLM 展现出了惊人的「抗性」。这并非偶然,而是根植于语言设计哲学与 LLM 运作机制之间的根本性张力。要理解这一现象,我们需要从 LLM 的本质 —— 模式识别 —— 出发,深入剖析 Lisp 的抽象机制如何恰好击中了这一弱点。
模式识别:LLM 编程能力的底层逻辑
现代大语言模型在代码生成方面的能力,源自其对大规模代码库的统计学习。在训练过程中,模型逐渐建立起对代码模式的敏感性:它能够识别出诸如资源分配与释放配对、条件分支的典型结构、API 调用的常见顺序等表面模式。这些模式在主流编程语言中呈现出高度的一致性。以 Python、Java、JavaScript 为代表的命令式语言中,函数调用分配资源后,通常会在同一作用域内显式调用对应的释放函数。LLM 正是在这种跨语言的共性中学会了「识别」这类模式,并将其迁移到新的代码生成任务中。
然而,这种学习方式存在一个根本性的假设:代码中的模式是可见的、驻留在源代码表面的。当我们审视一份典型的 C++ 或 Python 代码时,alloc 与 free、open 与 close、connect 与 disconnect 这些配对操作清晰可辨,模型可以有效地捕捉到它们的共现关系,并据此生成看似合理的代码。
抽象的代价:Lisp 宏如何「消除」模式
Lisp 程序员面对上述模式时的做法,与主流语言程序员截然不同。当 Lisp 程序员遇到需要配对使用的资源时,他们的第一反应不是编写冗长的配对代码,而是编写一个宏。以文件操作为例,Python 程序员会写出 with open 语句,但 Lisp 程序员会定义一个 with-open-file 宏,将资源获取、异常处理、清理释放全部封装在宏定义中。在实际使用点,程序员只需要调用这个宏,原始的模式便从源代码中彻底消失了。
这种抽象方式在 Lisp 中被发挥到了极致。Lisp 的宏系统不仅能够简化代码,更能够构建完整的领域特定语言。程序员可以将复杂的控制流、数据转换逻辑、甚至整个算法框架都压缩进宏的背后。这使得 Lisp 程序呈现出一种独特的面貌:源代码极其简洁,但每一行都可能蕴含着编译时展开后的数十乃至数百行代码。
问题在于,LLM 只能看到宏调用这一层「表面」,而宏展开后的代码才是真正执行的逻辑。当模型试图从源代码中提取模式时,它会发现这些代码与它在训练数据中看到的模式大相径庭。alloc/free 配对消失了,复杂的条件分支被压缩成了简单的宏调用,曾经清晰可见的控制流被隐藏在了抽象层级之下。
同象性与编译时求值:双重认知障碍
Lisp 的同象性(homoiconicity)是另一个加剧问题的因素。在 Lisp 中,程序本身即数据,S 表达式既是代码的语法形式,也是可以在运行时操作的数据结构。这意味着 Lisp 代码可以对自己的代码进行操作,可以动态地生成和修改代码。然而,LLM 的训练数据主要是程序的表面形式,而非经过宏展开或编译后的中间表示。模型从未有机会学习到宏展开后的代码模式,因为它在训练时接触的主要是用户编写的源代码。
同时,Lisp 宏的编译时求值特性进一步放大了这一困境。宏在编译阶段完成展开,运行时执行的是展开后的代码。这意味着源代码中的「模式」与实际执行的「行为」之间存在一道鸿沟。LLM 只能观察到表面的代码形式,却无法得知这些代码在实际运行时会变成什么样子。这种信息不对称使得模型难以做出正确的生成决策。
实践中的表现:LLM 生成 Lisp 代码的典型失败
根据已有的实验观察,LLM 在生成 Lisp 代码时会表现出一些典型的问题模式。当需要处理资源管理时,模型倾向于在每个代码分支中重复编写清理逻辑,而非使用已有的 with- 系列宏。它会在每个可能退出的路径上显式插入 deallocation 代码,即使这些逻辑本可以通过宏自动处理。这种生成方式不仅冗余,而且容易遗漏某些边界情况。
另一个常见问题是宏的误用。模型可能知道某些宏的存在,但却不理解它们的确切展开方式和使用场景。它可能生成语法正确的宏调用,但参数配置错误,或者在不适合使用宏的场合强行使用宏,导致代码行为与预期大相径庭。这些问题的根源都可以追溯到模型对「隐藏模式」的无力感知。
可能的应对策略
面对 Lisp 的「AI 抗性」,一些实践者开始探索应对之道。一种思路是在提示中包含宏展开后的代码示例,让模型直观地理解宏的语义。另一种思路是利用 MCP(Model Context Protocol)等工具,让 LLM 能够调用 Lisp 环境的求值功能,直接观察代码的执行结果,从而纠正自身的理解偏差。还有研究者提出双重训练策略:同时向模型展示源代码和对应的宏展开形式,帮助其建立两者之间的映射关系。
这些尝试在一定程度上能够缓解问题,但无法从根本上消除 Lisp 与 LLM 之间的张力。因为这种张力并非来自实现细节的缺陷,而是来自两种不同编程范式之间的深层冲突:一种是强调显式模式、依赖统计学习的 AI,另一种是强调抽象隐藏、依赖程序员智慧的符号系统。
Lisp 对 LLM 的「天然免疫」,本质上是语言设计哲学与机器学习范式之间的必然碰撞。它提醒我们,AI 辅助编程并非万能,其能力边界深受目标语言特性的影响。当一种语言的核心理念是「将模式消解于抽象之中」时,基于模式识别的 AI 工具自然会遭遇最大的挑战。
资料来源:Joe Marshall, "Why LLMs Suck at Lisp", Abstract Heresies (2025); arXiv:2506.10021, "From Tool Calling to Symbolic Thinking: LLMs in a Persistent Lisp Metaprogramming Loop" (2025).