项目特定的DSL是否是责任?

我从一个类似的问题中提出了这个问题,我在评论中提到了我收到的许多很棒的答案之一。 我最初询问AST宏,这主要引起了Lispers非常详细和深思熟虑的回应。 谢谢。

懒惰评估与宏

我在评论中提出的问题是项目特定的DSL是否真的是一个好主意。 当然,这是完全主观的 – 毕竟,当你用一种真正富有表现力的语言写作时,你在哪里画出富有表现力的API和实际的DSL? 例如,我认为大多数Rubyist称之为“DSL”的实际上只是精心设计的API而已。

请注意,我说项目特定的 API。 我认为很多人不会反对使用正则表达式或SQL这样做有意义。

但尽管如此,我认为我们都可以在API和DSL之间划清界限。 当然,它们都是真正的API,但无论如何。

在一个极端你有Lisp,似乎通过宏积极鼓励DSLs。 另一方面,你有像Java一样的DSL,这几乎是不可能的。

DSL的支持者认为它们可以提高灵活性,表现力并增加一致性(例如,使用与语言自身数字相同的运算符的自定义数字对象)。

批评者会说他们可以导致除了DSL编写者之外没有人知道的子语言,从根本上杀死了拥有不同编程语言的观点,并导致无人能理解的代码,因为与API 接口的方式不同。

我得说,我在很多方面同意双方的意见。 由于缺乏表现力,一些Java API只是简单的讨厌。 尽管如此,我通常可以在不阅读文档的情况下总结出正在发生的事情 – 这对于定制的DSL来说是丝毫不可能的。 也许DSL的支持者认为你应该总是阅读API文档。 我不同意,但我也离题了。

但是让我们来看看目前的一些大语言。 C#和Java,即。 他们都没有真正“做”DSL,但他们非常受欢迎。 这是因为他们不允许像DSL这样的东西,允许平庸的编码人员制作出仍然可以理解的代码吗?

事实上DSL是否允许平庸的编码器产生难以穿透的垃圾,这就是为什么Lisp没有尽可能多地使用它的原因,尽管DSL在右手中看起来像什么?

当然有支持DSL和反对它们的论点,当然在“库”或“API”和“DSL”之间存在模糊界限。 你在问题中已经涵盖了这一部分,所以我将避免这些主观观点,并专注于它们是否是一种责任的问题。

一个值得考虑的好项目是Racket ,它以语言结构为主要特征。 很容易为“语言”的任何定义提出一种语言:DSL或不是,通过解释器从近距离构成,或者(更常见地)通过定义新语言的宏来完成,并且可能是针对不同语法的解析器。 因此,Racket源代码树有一堆语言 – 其中一些语言具有根本不同的执行语义。 一些例子:

  • Lazy Racket就是你所期望的名字,

  • Typed Racket是一种静态类型语言,

  • Scribble是一种用于编写文档和其他散文的语言,

  • 幻灯片是一种用于写作的语言…幻灯片,

  • RackLog / DataLog是语义语法上更加不同的语言。

实际上,在Racket中编写语言非常容易,即使它适合非常有限的用途,甚至只是一个语言,你也可以轻松地编写一种语言。 例如,我们有这样的“小语言”,用于创建我们的网页,决定哪些文件包含在分布式安装程序中,还有更多。 有关如何提供语言的说明,请参阅本教程 。

确实,许多人可以使用的有用DSL和只有一个人使用的DSL之间存在一条细线 – 但是,当你定义一种语言而不是一个时,你可以得到的那种抽象是实质性的。指出它是一个有用的概念,即使它是一个“一人的语言”。 其中一个难题是考虑互操作性 – Racket允许每个模块都用自己的语言编写,这会带来当这些模块中的一些模块相互通信时会发生什么问题。 例如,当懒惰语言和默认语言中的函数交互时,评估是如何进行的; 或者如何键入以确保它可以与默认的无类型语言交互,并仍然获得静态类型语言的通常好处。

DSL广泛用于Java。 只是不是’内部’DSL。 与Java不同,Lisp有几种方法可以在不编写新外语的情况下改变语言语法和语义。 在Java中,大多数DSL都是外部的(通常基于XML)或使用预处理器实现。

如果您编写新的编程语言,也是特定于域的编程语言,则需要:

  • 指定它
  • 记录它
  • 测试一下
  • 使它健壮
  • 使它可调试
  • 让它变得高效……等等

Lisp并不是为你做这一切的灵丹妙药。 Lisp给你的是将一个或多个DSL直接包含在语言中,它允许你重用或改变Lisp工具来实现新语言的一部分。

Lisp在其历史上已经实现了许多语言。 其中许多只是研究语言而没有太多努力来遵循软件工程实践。 其中一些语言付出了更多的努力 – 例如,因为它们是产品的一部分。

如果您为项目开发语言,则需要确保遵循良好的软件工程实践,并且您有足够的资源来执行此操作。