DSL 独特之处

2008 年 12 月 22 日

关于编写外部领域特定语言 的一件事是,我正在走编程语言社区已经走过的路。编程语言研究一直是学术界的一个热门领域,我首先承认,我在这个主题上的深度远不及那些在这个领域研究多年的人。因此,不可避免地会有人问,为什么像我这样的新手认为自己可以在这个已经被踏平的领域写一本书呢?

主要原因是,还没有人写过关于 DSL 的面向实践者的书籍。我喜欢像这样已经被踏平但没有被很好地写过的主题。然而,当我花时间走这些路径时,我认为还有另一个因素在起作用。

有很多关于编程语言的研究,但几乎所有研究都集中在通用编程语言上。DSL 被视为通用编程思维的一个小而简单的子集。因此,人们认为通用语言的真实情况也适用于 DSL(这意味着 DSL 太小,不值得过多考虑)。

我越来越倾向于相反的结论。DSL 的规则不同于通用语言的规则——这在多个维度上都适用。

第一个是在语言设计方面。我曾与一位我非常尊敬的语言设计师交谈,他强调语言的一个关键特征是能够定义新的抽象。我认为 DSL 不是这样。在大多数 DSL 中,DSL 选择您使用的抽象,如果您想要不同的抽象,您将使用不同的 DSL(或者可能扩展您正在使用的 DSL)。有时,新的抽象会发挥作用,但这些情况是少数,而且当它们发生时,抽象是有限的。事实上,我认为缺乏定义新抽象的能力是 DSL 与通用语言的区别之一。

差异也出现在您用于实现语言相关工具的方法中。通用语言的一个持续问题是处理大型输入,因为现实程序将包含数千或数百万行代码。因此,许多用于使用它们的工具和技术都包含一些方面,这些方面使解析更难理解,但支持这些大型输入。DSL 脚本往往要小得多,因此这些权衡以不同的方式起作用。

在我的工作中,我非常重视使用 DSL 来填充语义模型,并使用该模型作为任何进一步处理的基础:解释、可视化或代码生成。我看到很多语言编写都倾向于强调代码生成,通常直接从语法文件生成代码。中间表示没有被太多提及,而且当它们出现时,它们更多地以抽象语法树的形式出现,而不是语义模型。严肃的编译器确实使用中间表示,例如程序依赖图,但这些被认为(正确地)是高级主题。我认为语义模型是简化 DSL 使用的一个非常有价值的工具,它允许您将解析与语义分离。

由于 DSL 的表达能力较弱,因此您可以为它们设计更简单的语言。语言社区的大部分写作都谈论如何处理复杂通用语言的困难,而 DSL 的挑战是编写一种语言,这种语言对目标受众(可能包括非程序员)可读,并且易于解析(以简化解析器的维护)。这不仅会导致语言设计方面的不同决策,而且还意味着您实际上只需要解析器生成器的子集功能。

其结果是,DSL 的编写是基于这样的预期,即每个单独的 DSL 不会解决手头的全部问题,并且您经常需要组合 DSL。传统的语言思维并没有太多地探索可组合语言的想法,但我认为这个主题在 DSL 发展中非常重要。考虑可组合语言应该对语言设计和语言工具产生重大影响。

因此,我越来越倾向于认为 DSL 激发了一些关于编程语言的截然不同的思维方式。它也可能导致开发更适合 DSL 工作的解析工具——通常是更简单的工具。我希望 DSL 如今受到的越来越多的关注将导致更多人将 DSL 视为学习的一级主题,而不是通用语言的简单形式。