把扩展能力带到更多程序

GNU Guile的早期工作围绕一个实际问题:应用程序的用户怎样改变软件行为,而不必每次都重写底层程序。GNU Emacs已通过Lisp提供这种能力。1994年10月19日,理查德·斯托尔曼在《GNU扩展语言计划》中提出,让GNU程序尽可能共享扩展语言,使使用者学到的知识能够跨程序使用。

公告所谈的选择权也很具体。若一个程序只嵌入某一种解释器,用户便只能用作者选定的语言写扩展。斯托尔曼希望建立共同的执行基础,再在它上面实现不同语言。这份有日期的公开设计,与随后可下载的发行版本属于不同阶段。

参考:[1]

已有解释器与嵌入改造

汤姆·洛德承担了早期嵌入工作的核心部分。他把奥布里·贾弗的SCM解释器改造成能供别的程序链接的库,项目起初称为GNU扩展语言GEL,后来改名Guile。SCM本身又承接乔治·卡雷特的SIOD解释器,形成一条有具体作者的实现谱系。

Guile 1.0的贡献者说明保留了这种分工:洛德负责把SCM库化,并连接Tcl/Tk及正则表达式工具;贾弗贡献性能和解释器内部机制;加里·休斯敦参与Unix系统调用支持。到1.0时,主要维护者为吉姆·布兰迪,马克·加拉西则参与高层接口和手册。

参考:[3] [4]

设计目标与实施路线

1994年的计划以修改过的Scheme为基础,列出异常处理、字符串操作、Unix系统调用以及调用C代码的接口,再考虑把其他语言翻译到这个基础上。公告还向熟悉Scheme实现的志愿者征集帮助,并以洛德作为联系者。这是一项组织开发工作的方案,而非宣告所有目标已经完成。

嵌入意味着应用程序仍能保留自己的C代码,同时交给用户一个可解释执行的高层语言环境。早期使用说明要求程序链接libguile.a,并分别介绍初始化、执行字符串或文件、定义可从Scheme调用的C函数。配置规则、交互命令和较复杂的扩展,因此能够与底层实现分开编写。

参考:[1] [2]

1995年的发行与模块拆分

维护者温戈后来整理的发布表把guile-i记为1995年2月4日,把guile-ii记为4月6日;后者增加低层模块系统和Tcl/Tk支持。Guile 1.0所附早期用户手册也把guile-ii置于1995年春,把guile-iii置于同年夏季,说明公开计划之后已有连续的开发发行。

guile-iii发行包的公告强调,先前集中在一起的功能开始拆成附加包。用户可将Tcl/Tk相关包放进同一源代码树,再按configure、make的过程构建所需组合。这种组织方式回应了两类需求:一些人要较小的解释器,另一些人需要图形工具等较多功能。

参考:[2] [3] [4]

一个C与Scheme互相调用的例子

1997年Guile 1.0发行包的gh_test_c.c给出了具体演示。C程序通过gh_enter启动解释器,再用gh_eval_str交入Scheme代码;其中先定义平方函数,随后求square 9。按这段定义,运算所得为81。程序并不另造一套表达式解析器,而是把表达式交给已有语言环境。

另一个方向则由c_sin函数展示:它将Scheme数值转成C的double,调用数学库的sin,再把结果转回Scheme对象。程序用gh_new_procedure1_0把它登记为名为c_sin的单参数过程。如此,用户写Scheme扩展时就能使用应用程序提供的C功能;调用两端各自负责数据转换和实际计算。

参考:[3]

整合、取舍与后续分流

Guile 1.0的README称它是自由软件基金会制作的第一个Guile发行版,同时明确承认Cygnus Support已有更早的版本。因此“1.0”所标记的是一轮维护与整合,不是此前工作从未公开。其NEWS还记录将假值与空表重新分开等调整,显示最初为兼容其他Lisp而作的选择会在使用中修订。

Kawa项目的参与者回顾,Cygnus在1995年接到增强Guile的合同,线程等工作也由不同开发者承担;后来客户同意转向基于Java的实现路线,Kawa于1996年继续发展。Guile与这条分流并行延续,其早期历史由公告、发行包和维护交接共同构成。

参考:[3] [5]