集成污染

写过《高级动态语言与软件业》之后,Alex 回复说 Lua 语言应该符合我的期望。其实我应该早就意识到 Lua 的存在,因为 Lightroom 就是用它作为 policy 部分的编程语言。只是 Lua 的库不如 Python 等语言强大,作为编写应用程序的主力语言还显单薄。但正是因此使用轻量级的 Lua 的开发者很少受到诱惑用它来开发复杂算法和 policy 之外的东西(比如 UI ),那正是我在《高级动态语言与软件业》里所反对的。

另一方面,作为描述算法和 policy 的动态语言的另一个重要条件在《高级动态语言与软件业》里讨论不多,却正是 Lua 的优势 —— Lua 是我所知的造成『集成污染』最少的库。

什么叫做『集成污染』?简单地说,如果系统的构建( build )在加入某个模块之前一直好好的,加入这个摸块之后就开始出现问题,这就叫集成污染。这种症状在编译、链接和运行阶段都可能发生。例如,编译阶段的集成污染的可以来自 C 的 macro ,typedef ,条件编译,C++ 的 template 。运行时的集成污染可能来自不同的多线程模型混用。

一个模块能否被顺利地集成到更大的系统中,取决于它希望别的模块如何对待它。一个模块对其它模块的要求越多,越不明确,集成的困难就越大。当这些要求被错误地理解或者无法满足的时候,就变成了集成污染。如果一个模块只是对数据的类型有所要求,那么任何一个可以定义抽象数据结构的静态语言都能很容易的描述这种要求,而且也很容易得到满足。但是,即使是如此简单的需求,也会遇到命名冲突之类的问题。通常这就是 macro 和 typedef 造成集成污染的原因。

接下来,有些模块的要求在『理论上』能够被满足,但是这种要求超出了现有技术的表达能力。C++ template 和多线程就是这样的例子。一个使用了 template 的模块很难用 C++ 语言本身的结构来说明自己的需求。对多线程来说情况就更严重,因为不仅编程语言不能描述线程模型,连数学语言和自然语言也很难清晰描述一个模型对线程使用的期望。所以,基本上集成涉及多线程的模块是一种盲目试错的过程。

更进一步,有些模块的要求是否能被清晰地描述已经不重要,因为这些要求常常是难以满足的。这些模块要求独占 system-wide 稀缺资源。比如,Cocoa 框架把整个应用的很多初始化工作和主消息循环封装到一个黑箱函数 NSApplicationMain() 中,古老的 C++ Framework 如 MFC 也会隐藏 main() 函数。这些库用自己的方式任意使用消息循环和主函数。如此霸道的模块,在系统中是一山不容二虎。《高级动态语言与软件业》提到很多高级动态语言和静态语言的互操作方式仅限于单向发起 —— 能调用静态语言编写的动态链接库,但是不能作为库被其它(静态)语言调用,这就等同于对主函数(有时连同消息循环)的独占。

解决集成污染的方法是各种边界分离。像 macro 、typedef 之类的命名污染可以通过文件分离来解决。终极方法是进程分离加 IPC 。不论如何难缠的集成污染也会在进程边界止步。另一种解决之道是参与集成的模块低调、收敛、谦虚一些。这就是 Lua 的方式。采用 ANSI C 中最成熟的跨平台部分,避免了编译时的错误。放弃对线程和 I/O 的支持,避免了运行时污染。Lua 能做这样的取舍,原因在于有合理的目标,把自己定位为描述算法的符号操作语言,和 hosting 应用的交互限于严格而且有序的符号集合。这让 Lua 成为扩展 C 语言和的方案中最简洁和最可靠的形式。

留下评论