11

我正在做一个 Clojure 项目,我经常发现自己为 DSL 编写 Clojure 宏,但我正在观看有关公司如何在实际工作中使用 Clojure 的 Clojure 视频,演讲者说在实际使用中他们不使用宏DSL,它们只使用宏来添加一点语法糖。这是否意味着我应该使用标准函数编写我的 DSL,然后在最后添加一些宏?

更新:在阅读了对这个问题的许多不同(和有趣)的回答后,我意识到答案并不像我最初想象的那样明确,原因有很多:

  1. 应用程序中有许多不同类型的 API(内部、外部)

  2. API 有多种类型的用户(只想快速完成某事的业务用户,Clojure 专家)

  3. 那里有隐藏样板代码的宏吗?

我会离开并更深入地思考这个问题,但感谢您的回答,因为他们给了我很多思考。我还注意到 Paul Graham 的想法与 Christophe 的视频相反,他认为宏应该是代码库的很大一部分(25%):

http://www.paulgraham.com/avg.html

4

4 回答 4

12

在某种程度上,我相信这取决于您的 DSL 的用途/目的。

如果您正在编写一个类似库的 DSL 以在 Clojure 代码中使用并希望它以一种函数式的方式使用,那么我更喜欢函数而不是宏。函数对 Clojure 用户来说是“不错的”,因为它们可以动态组合成更高阶的函数等。例如,您正在编写像Ring这样的函数式 Web 框架。

如果您正在编写一个命令式 DSL,它将完全独立于其他 Clojure 代码使用,并且您确定您绝对不需要高阶函数,那么用法将非常相似,您可以选择最有意义的那个。例如,您可能正在创建某种业务规则引擎。

如果您正在编写需要生成高性能代码的专用 DSL,那么您可能大部分时间都希望使用宏,因为它们将在编译时进行扩展以获得最大效率。例如,您正在编写一些图形代码,需要扩展为完全正确的 OpenGL 调用序列......

于 2011-03-28T12:57:50.133 回答
7

是的!

尽可能编写函数。当函数可以执行时,切勿编写宏。如果你写了很多宏,你最终会得到一些更难扩展的东西。例如,不能应用或传递宏。

Christophe Grand:(不 = DSL 宏)

http://clojure.blip.tv/file/4522250/

于 2011-03-28T10:09:49.457 回答
3

不!

不要害怕广泛使用宏。如有疑问,请务必编写宏。函数在实现 DSL 方面较差——它们将负担转移到运行时,而宏允许在编译时间内进行许多重量级计算。试想一下将嵌入式 Prolog 实现为解释器函数和将 Prolog 编译为某种形式的 WAM 的宏的区别。

而且不要听那些说“宏不能应用或传递”的人,这种说法完全是稻草人。那些人提倡解释器而不是编译器,这简直是荒谬的。

关于如何使用宏实现 DSL 的一些技巧:

  • 分阶段进行。定义从 DSL 到底层 Clojure 的一长串语言。使每个转换尽可能简单——这样您就可以轻松地维护和调试您的 DSL 编译器。
  • 准备一个 DSL 组件工具箱,在实现 DSL 时将重用这些组件。它应该包括不同语义的目标语言(例如,无类型的急切函数 - 它是 Clojure 本身、无类型的惰性函数、一阶逻辑、类型化的命令式、Hindley-Millner 类型的急切函数、数据流等)。使用宏将所有目标语义的属性无缝组合是微不足道的。
  • 维护一组编译器构建工具。它应该包括解析器生成器(即使您的 DSL 完全在 S 表达式中也很有用)、术语重写引擎、模式匹配引擎、图上一些常见算法的实现(例如,图着色)等。
于 2011-03-28T10:37:11.393 回答
2

这是 Haskell 中使用函数而不是宏的 DSL 示例:

http://contracts.scheming.org/

以下是 Simon Peyton Jones 的视频,介绍了此实现:

http://ulf.wiger.net/weblog/2008/02/29/simon-peyton-jones-composing-contracts-an-adventure-in-financial-engineering/

在走上实现自己的语言的道路之前,利用 Clojure 和 FP 的特性。我认为 SK-logic 的提示可以很好地说明实现完整语言所需的条件。有时值得付出努力,但这种情况很少见。

于 2011-03-28T12:19:33.760 回答