《The Evolution of Lua》翻译part 2

Lua2

1990年的时候,面向对象迈向巅峰,对于Lua没有面向对象的支持,我们受到了很大的压力。我们不想将Lua变成面向对象,因为我们不想“修复”一种编程范式(fix a programming paradigm)。特别是,我们不觉得Lua需要将对象和类作为基础语言概念,我们觉得可以透过table来实现(table可以保存方法和数据,因为函数是第一类对象)。直到今天,Lua也没有强加任何对象和类模型给用户,我们初心不变。很多用户建议和实现了面向对象模型;面向对象也是邮件列表里经常讨论的问题,我们觉得这是健康的。

另一方面,我们希望允许Lua可以面向对象编程。我们决定提供一套灵活的机制,让用户可以选择对应用来说合适的模型,而不是修复模型。1995年2月发布的Lua2.1,标志着这些灵活语义的问世,极大的增加了Lua的表达能力,从此,灵活语义就变成了Lua的标志。

灵活语义的一个目标是允许table作为对象和类的基础。为了实现这个目标,我们需要实现table的继承。另一个目标是将userdata变成应用数据的天然代理,可以作为函数参数而不只是一个句柄。我们希望能够索引userdata,就好像他们只是一个table,可供调用他们身上的方法。这让Lua可以更为自然的实现自己的主要设计目标:通过提供脚本访问到应用服务和数据,从而扩展应用。我们决定实现一套fallback机制,让Lua把未定义行为交给程序员处理,而不是直接在语言本身实现这些特性。

在Lua2.1的时候,我们提供了fallback机制,支持以下行为:table索引,算术操作符,字符串拼接,顺序比较,函数调用。当这些操作应用到“错误”的类型上,对应的fallback就会被调用到,允许程序员决定Lua如何处理。table索引fallback允许userdata和其它值类型表现的跟表一样。我们也定义了当Key不在table时的fallback,从而实现多种形式的继承(通过委托)。为了完善面向对象编程,我们添加了两个语法糖:function a:foo(…)就好比function a.foo(self,…)一样,以及a:foo(…)作为a.foo(a, …)的语法糖。在6.8节我们会讨论fallback的细节。

从Lua1.0开始,我们就提供了值类型的内省函数(introspective functions):type,可以用来获取Lua值的类型;next,可以用来遍历一个table;以及nextvar,可以遍历全局环境。(正如第四章所述,这是为了实现类似SOL的类型检查)为了应付用户对完整调试设施的强烈需求,1995年12月发布的Lua2.2引入了一个debug API来获取运行中的函数信息。这个API为用户提供了以C语言编写自己的内省函数工具链的手段,比如编写自己的调试器和性能分析工具。debug API刚开始的时候相当简洁:debug库允许访问Lua的调用栈,访问当前执行的代码行行数,以及一个可以查找指定值的变量名的函数。根据M.Sc.的Tomas Gorham的工作,debug API在1996年5月发布的Lua2.4版本里得到了完善,提供了函数访问局部变量,提供了钩子在行数变化和函数调用时触发。

因为Lua在Tecgraf的广泛使用,很多大型的图形源文件都是用Lua写的,作为图形编辑器的输出格式。加载这些源文件会随着文件大小变得越来越大和越来越复杂而变的越来越长。从第一版开始,Lua就预编译所有程序到字节码,再执行字节码。大型代码文件的加载时间可以通过保存bytecode来缩减。这和处理图形源文件特别有关系。所以在Lua 2.4版本,我们引入了一个外部编译器Luac,可以编译一个Lua文件为字节码并保存为二进制文件。这个二进制文件的格式经过精心选择,可以轻松加载同时体积小巧。通过luac,程序员可以在运行期避免词法分析和代码生成,这些操作早期还是比较耗时的。除了更快的加载,luac还允许离线的语法检查,以及随意的用户改动。很多产品(比如模拟人生和Adobe的Lightroom)都是透过预编译格式发布Lua脚本的。

在luac的实现过程里,我们开始将Lua的核心重构成清晰的分离模块。于是,我们现在可以轻易移除词法解析的模块(词法解析器,语法解析器和代码生成器),这些部分占了大约35%的核心代码,剩下的部分可以加载预编译的Lua脚本。这种代码剪裁,对于在移动设备、机器人和感应器这些小设备里嵌入Lua,是有显著意义的。

从第一版开始,Lua就自带有一个库来进行字符串处理。这个库在Lua2.4之前功能有限。但是,随着Lua的成熟,Lua需要进行更重量级的字符串处理。我们认为,沿革Snobol,Icon,Awk和Perl的传统,为Lua添加模式匹配是自然而然的。但是,我们不想将第三方的模式匹配引擎打包到Lua里面去,因为这些引擎通常都很大,我们也希望避开因为引入第三方库带来的代码版权问题。

1995年的第二学期,作为Roberto指导下的学生项目,Milton Jonathan,Pedro Miller Rabinovitch,Pedro Willemsens和Vinicius Almendra为Lua写出了一个模式匹配库。那个设计的经验引导我们写出了我们自己的模式匹配引擎。1996年的12月,我们在Lua2.5中添加了两个函数:strfind(最早职能查找纯文本)和新的gsub函数(名字来源于Awk)。gsub函数可以用来全局替换符合指定模式的子串。它接受一个新的字符串或者一个函数作为参数,函数参数会在每次遇到匹配时调用,并预期该函数返回新子串以供替换。为了缩小实现的规模,我们没有支持完整的正则表达式。我们支持的模式包括字符类,重复,以及捕获(但是没有可选或组匹配)除了简洁性,这种模式匹配还十分强大,是Lua的一个强有力的补充。

那一年是Lua历史上的转折点,因为Lua获得了全球的曝光。在1996年6月,我们在《Software:Practice & Experience》杂志上发布了一篇Lua的文章,为Lua带来了外部的关注,至少是学术圈子的关注。在1996年的12月,Lua 2.5刚刚发布后,杂志Dr.Dobb’s Journal也发表了Lua的文章。Dr.Dobb’s Journal是一本面向程序员的流行刊物,那篇文章为Lua带去了软件工业界的关注。在那次发布之后,我们收到了很多消息,其中一条是1997年1月收到的,来自Bret Mogilefsky,LucasArts出品的冒险游戏——Grim Fandango的首席程序员。Bret告诉我们,他从Dr.Dobb’s上读到了Lua,他们打算用Lua代替自己写的脚本语言。1998年10月,Grim Fandango发布,1999年5月Bret告诉我们“大量的游戏都是用Lua写的”。那个时候,Bret参加了GDC(Game Developers’ Conference, 游戏开发者会议)的有关游戏脚本的圆桌会议。他谈到了Grim Fandango应用Lua的成功经验。很多我们认识的开发者,都是在那次事件里认识到Lua的。在那以后,Lua在游戏程序员之间口耳相传,变成了游戏工业里可资销售的技能。

因为Lua的国际化曝光,向我们提问Lua的信息越来越多。1997年2月,我们建立了邮件列表,好更有效率的处理这些问题,让其他人也能帮忙回答问题,并开始建设Lua社区。这个列表至今发布了超过38000条消息。很多热门游戏对Lua的使用,吸引了很多人到邮件列表里。现在已经有超过1200个订阅者了。Lua列表十分友善,同时又富有技术性,我们对此深感幸运。邮件列表逐渐变成了Lua社区的焦点所在,也是Lua演进的动力源泉。所有的重大事件,都是首先在邮件列表里出现的:发布重大通知,请求新特性,bug报告等等。

在Usenet新闻组里建立comp.lang.lua讨论组曾经在邮件列表里讨论过两次,分别是1998年的4月和1999年的7月。两次的结论都是邮件列表的流量不能保证创建一个新闻组。而且,更多人倾向于邮件列表。随着多个阅读和搜索完整邮件存档的web界面问世,创建新闻组变得无关紧要了。