在编程语言发展的长河中,有些项目因其开创性而被铭记,有些则因生不逢时而被尘封。2008年左右,一位匿名的开发者发布了一款名为“Lithp.py”的实验性Python库,试图在Python中嵌入一个近似Lisp语法的方言。这个仅有一千余行代码的项目,既是对“可编程编程语言”理念的致敬,也折射出那个时代Python社区对元编程的探索热情。
起源:当Python遇见Lisp
Lithp之名显然取自“Lisp”的谐音。2008年,Python 2.5/2.6盛行,Python的装饰器、描述符、eval与exec等动态特性已相当成熟,但鲜有人将其与Lisp的“代码即数据”哲学深度结合。据项目早期README文档显示,Lithp的开发者深受Paul Graham《On Lisp》和Peter Norvig《Paradigms of Artificial Intelligence Programming》中Lisp元循环求值器(meta-circular evaluator)的启发,希望用Python实现一个“可直接在Python运行时中运行的Lisp子集”。
与当时已有的Hy(一个完整的Lisp方言嵌入Python)不同,Lithp的设计目标并非打造独立语言,而是充当“语法糖”和“宏工厂”:开发者可以在Python字符串中书写类似Lisp的S表达式,然后通过Lithp的解释器将其转换为Python可执行对象。
核心机制:S表达式到Python AST的桥梁
Lithp的代码库核心由三部分组成:
-
解析器:一个约200行的递归下降解析器,能够处理基本的S表达式(
(+ 1 2)、(define x 3)等),并生成Python的列表嵌套结构。它不支持宏,但允许在表达式内直接嵌入Python对象(通过!前缀转义)。 -
环境模型:一个基于Python字典的作用域链,支持
lambda、define、if等基本结构。其中lambda被翻译为Python的匿名函数,闭包得以自然实现。 -
求值器:最有趣的部分。Lithp的求值器并非简单调用
eval(),而是递归地遍历列表结构,将S表达式映射为Python的AST节点(通过ast模块),最后编译并执行。这种设计使得Lithp生成的代码与原生Python字节码无异,避免了性能陷阱。
一个典型的使用示例(摘自项目示例文件):
import lithp
# 定义一个斐波那契函数
expr = """
(define fib
(lambda (n)
(if (< n 2)
n
(+ (fib (- n 1)) (fib (- n 2))))))
"""
lithp.eval(expr)
print(lithp.eval("(fib 10)")) # 输出55
该项目还支持“混合模式”——在Python函数内部以字符串形式嵌入Lisp代码,通过lithp.eval()动态求值,这为某些需要高度灵活性的领域(如配置框架、游戏AI决策树)提供了有趣的方案。
历史坐标:为什么它没有流行?
Lithp出现在一个有趣的过渡期:2008年Python社区正处于向Python 3过渡的阵痛中,而新兴的“DSL(领域特定语言)”概念尚未普及。尽管Lithp展示了Python元编程的潜力,但它的局限性同样明显:
- 错误信息晦涩:由于经过两次转换(S表达式→AST→字节码),运行时错误的回溯指向Lithp内部的解析栈,而非用户源码。
- 性能损失:每次
lithp.eval()都触发完整的解析—编译—求值流程,循环中调用存在巨大开销。 - 社区支持缺位:项目仅发布在SourceForge和个人的Trac Wiki上,无文档、无测试、无持续集成,很快便无人问津。
对比之下,2012年出现的Hy语言(也是Lisp嵌入Python)采用了更彻底的方案:独立的编译器、完整的语法高亮、pip安装支持,从而获得了更长的生命周期。Lithp作为先驱,却因“过于轻盈”而未能进入主流视野。
遗产与启示
尽管Lithp.py最终在2010年左右停止更新,它的代码库至今仍可运行(需Python 2.7或兼容模式下使用)。该项目散见于GitHub上的几个被fork的仓库中,偶尔有爱好者将其移植到Python 3,或作为教学案例展示“如何实现一个微型语言”。
从今天的视角回望,Lithp的意义或许不在于实用,而在于它验证了一个关键命题:即便不使用复杂的宏系统,仅凭Python已有的动态特性就能模拟Lisp的核心求值语义。它对后来许多项目——比如用于配置的lispy库、用于机器人的行为树DSL——产生了隐性影响。
一位曾参与维护的开发者(化名“f0x”)在技术博客中写道:“Lithp是个可耻的玩具,但它让我第一次理解了求值器与AST之间的关系。它为后来我用Python编写自定义语言解释器打下了基础。”
在技术快速迭代的今天,Lithp.py或许只是一段被遗忘的代码——如同博物馆里的一台老式终端。但它提醒我们:每一次先锋实验,无论成败,都为后来的创新铺就了道路。正如那句编程格言:任何足够有趣的玩具,最终都会成为一门语言。