【问题标题】:Are Project-Specific DSLs a Liability? [closed]特定于项目的 DSL 是一种责任吗? [关闭]
【发布时间】:2011-08-13 20:09:15
【问题描述】:

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

Lazy Evaluation vs Macros

我在评论中提出的问题是,特定于项目的 DSL 是否真的是一个好主意。当然,这完全是主观的——毕竟,当你用一种真正富有表现力的语言编写代码时,你在哪里划定了富有表现力的 API 和实际的 DSL 之间的界限?例如,我认为大多数 Ruby 主义者所说的“DSL”实际上只是精心设计的 API,仅此而已。

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

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

在一个极端情况下,你有 Lisp,其中似乎通过宏积极鼓励 DSL。另一方面,你有 Java 之类的东西,DSL 几乎是不可能的。

DSL 的支持者会争辩说,它们增加了灵活性、表现力和一致性(例如,使用与语言自己的数字相同的运算符的自定义数字对象)。

批评者会说他们会导致除了 DSL 编写者之外没有人知道的子语言,首先扼杀了使用不同编程语言的意义,并导致没有人可以理解的代码,因为 接口的方式 与 API 不同。

我得说,我在很多方面都同意双方的观点。由于缺乏表现力,一些 Java API 简直就是令人讨厌。尽管如此,我通常可以在不阅读文档的情况下弄清楚发生了什么——这对于自定义 DSL 一点也不能说。也许 DSL 支持者认为您应该始终阅读 API 文档。我不同意,但我也离题了。

但是,让我们看看目前的一些主要语言。 C#和Java,即。它们都没有真正“做” DSL,但它们非常受欢迎。这是否正是因为他们不允许允许使用 DSL 之类的东西,从而允许平庸的编码人员编写出仍然可以理解的代码?

DSL 允许平庸的编码人员产生难以理解的垃圾这一事实是否是 Lisp 没有得到应有的使用的原因,尽管 DSL 在右手中看起来会是什么样子?

【问题讨论】:

  • 我认为如果你在创建 DSL 方面做得很好,无论如何它都会超越单个项目:)
  • @Affe:非常真实,哈哈。但公平地说,像正则表达式和 SQL 一样,能够深入到每个程序员必须学习的列表中的 DSL 也只有这么多。
  • 谁说不能用Java来创建DSL? camel.apache.org/dsl.html
  • @duffymo:你的链接证明了我的观点:你不能在 Java 中做 DSL,所以他们必须结合语法在字符串和链式方法调用来实现 DSL,它似乎。比你在 Haskell 或 Lisp 中得到的东西笨拙得多,而且更容易出错。
  • 我刚刚查看了 Xtext Wikipedia 页面,看到了这样的声明:“与标准解析器生成器不同,Xtext 不仅生成解析器,[...]”。这意味着它必须解析它,这反过来又意味着它不是 Java。用 Java 编写一个完整的解析器来解释 DSL 表明 Java 不擅长 DSL 构造。

标签: java api macros lisp dsl


【解决方案1】:

当然有支持和反对 DSL 的论据,当然“库”或“API”与“DSL”之间的界限很模糊。你在问题中已经很好地涵盖了这部分,所以我将避免这些主观观点,而只关注它们是否是责任的问题。

一个值得考虑的好项目是Racket,它将语言构造作为其主要特征。很容易为“一种语言”的任何定义添加一种语言:DSL 与否,通过解释器从头开始构建,或者(更常见)通过定义新语言的宏以及可能用于不同语法的解析器来完成。因此,Racket 源代码树有一堆语言——其中一些具有根本不同的执行语义。一些例子:

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

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

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

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

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

事实上,在 Racket 中编造语言非常容易,即使它适合非常有限的用途,甚至只是一种语言,您也可以轻松地拼凑出一种语言。例如,我们有这样的“小语言”用于创建我们的网页,决定哪些文件包含在分布式安装程序中,等等。请参阅 this tutorial 了解如何提出一种语言的说明。

确实,在许多人都可以使用的有用 DSL 和只有一个人使用的 DSL 之间有一条细线——但仍然是定义一种语言时可以获得的抽象类型 而不是 图书馆 是实质性的,以至于它是一个有用的概念,即使它是“一个人的语言”。这方面的一个难题是考虑互操作性——Racket 允许每个模块用它自己的语言编写,这就带来了当这些模块中的几个应该相互通信时会发生什么的问题。例如,当惰性语言和默认语言中的函数交互时,评估如何进行?或者类型化如何确保它可以与默认的非类型化语言交互,并且仍然可以获得静态类型化语言的通常好处。

【讨论】:

    【解决方案2】:

    DSL 在 Java 中被广泛使用。只是不是“内部”DSL。与 Java 不同,Lisp 有几种方法可以改变语言的语法和语义,而无需编写新的外部语言。在 Java 中,大多数 DSL 要么是外部的(通常基于 XML),要么是通过预处理器实现的。

    如果您编写一种新的编程语言,也是一种特定领域的编程语言,您需要:

    • 指定它
    • 记录下来
    • 测试一下
    • 使其健壮
    • 使其可调试
    • 使其高效 ...等等

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

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

    如果您为您的项目开发一种语言,您需要确保您遵循良好的软件工程实践并且您拥有这样做的资源。

    【讨论】:

    • Lisp 确实有一个灵丹妙药:宏的全部意义在于它们是一种 local 类型的转换,它允许重用语言的其余部分,这很明显与需要投入更多工作的非 Lisp 语言相比。 (有关此主题,另请参阅 Felleisen's paper。)
    • 如果 Java DSL 必须通过预处理器或 XML 完成,那么根据定义,它不是 Java DSL。但我明白了你帖子的一般观点,但我只想说我并没有开始一场 DSL-in-Lisp 与 DSL-in-Java 的激烈争吵,更多的是关于 DSL 的可行性本身,你的要点涵盖了很好。
    • @Louis,什么是“Java DSL”?那是什么定义?
    • 在编译时直接通过 Javacc 的解析器解析的 DSL。即,不会像许多 DSLs-in-strings 那样被手动解析器解析,也不会被任何解析器到反射库解析。代码生成器也不计算在内。它必须由语言自己的解析器进行解析,而无需事先进行任何转换。
    • 这是我的个人定义;我敢肯定,有许多 Java 程序员认为字符串中的语言或代码生成器实际上是 DSL。他们错了:p
    猜你喜欢
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 2010-09-25
    • 2021-01-06
    • 1970-01-01
    • 1970-01-01
    • 2011-09-26
    • 2020-04-17
    相关资源
    最近更新 更多