Saturday, July 15, 2006
Schema Analysis and the need for Python-based DSLs
最近,我偶然碰到了一个有意思的项目:
“Alloy分析器是由软件设计组(Software Design Group)开发的一个用来分析使用Alloy建立的模型的工具。Alloy是一个简单的基于一阶逻辑的结构建模语言。该工具可以生成不变式的实例、模拟操作的执行(即使是那些隐式定义的操作)和检查一个模型的用户指定属性。Alloy和他的分析器起初常被用来研究抽象的软件设计。在后续的研究项目中它还被用于分析代码与规格说明书的一致性和作为一个测试用例的自动生成器来使用。”
我花了一点时间仔细阅读了其用户手册,发现这个语言与相关领域计算法(domain-relational calculus)十分相像。它用居于集合和关系两者之间的形式来表现一个模式。实际上,它看起来就是一个很棒的用来设计对象模型并表达它们内在约束关系的语言。我甚至都想知道能否用一个精美的Python式语法,寻找到一种扩充表达prolog-like规则的Python食谱秘诀(Python cookbook recipe for expressing prolog-like rules)的途径以支持Alloy的全部表达能力。
简洁地用纯Python来表现模式(expressing schemas)的最大的一个问题是你常常需要在类型定义前涉及到这些类型。例如,如果你希望FileSystemObject将Directory作为其父类,而Directory是FileSystemObject的其中一子类,这时你就得使用一个向前引用,然而这是用今天的Python很难轻松表达出来的。凭借我为Chandler和PEAK编写的模式定义工具来解决这个问题要么是需要后一个类型(Directory)定义一个反向关系再链接到前向关系,要么是使用字符串来引用未定义的类型。但不管使用哪种方法都不是十分令人满意。
所以高明的食谱秘籍(cookbook recipe)的优势就在于它只是让一个函数体中的所有命名都具有象征性的意思,自动地为这些命名创建对象并在其范围之内执行函数代码。之后,传统的操作符重载技术就可以从那接管过来。这就好比开发人员可以用它来这样定义一个模式:
@schema
def foo():
+ FSObject(parent = Dir)
+ Dir(contents = set [FSObject]) < FSObject
+ File < FSObject
好了,如此看来结果真的不怎么漂亮。实际上,它唯一的好处是其允许向前引用。但我打算好好研究一下Alloy相关用户手册,看看能否找到一个更自然一些的从其概念到Python的映射关系。与尝试去简明地表达类似Python中 “directory不可以是其自身的子类”一类的通用集合约束(generalized set constraints)这样一个问题比起来,向前引用的问题实际上真的是次要的了。这都只是一些需要去实现我的关于peak.schema的乌托邦梦想(utopian dreams for peak.schema)的东西,而它们需要一个逻辑或set (mini-)语言来甄选出来。
遗憾的是,Python并非是这世界上用于创造特殊域(domain-specific)语言中的最适合的语言。在这方面Ruby确实要强于Python—这可不同于那些Ruby的狂热爱好者们所追捧的所有所谓的优点,他们没意识到Python早已具有那些他们都还含含糊糊说不清的基本要素。
然而,Ruby在这一方面的优点基本可归结于两点:无需括号括起就可以调用函数和拥有代码块。第一个优点很好,因为它实现了创建命令或伪语句(pseudo-statements),第二个也有必要,因为它使那些伪语句能够包含代码块。假如至少没有第二个特性,Python也将不再适用于重量级的DSL构建。
然而,出于考虑到人们可能写出令人困惑的代码的原因,块大概是Python永远也不会引入的一项机制了。也就是说,让每一位程序员成为一个语言设计者就意味着每一个程序都变成是用它自己的语言所写的了:灾难性的急剧滑向Lisp。:-)Python之所以伟大的原因之一就是它是一个简单,易学的语言。有一点“一次学会,通读所有(learn once, read anything)”的意思。可定制的语法意味着在其退化的情况下可以变成一种新语言,有了它你能够理解你想要读的任何程序。说实话我不是很确定该不该打开这些闸门,尽管它偶尔会因为无法创建DSLs而给我带来不便。
(原文链接网址:http://dirtsimple.org/2006/07/schema-analysis-and-need-for-python.html)