【问题标题】:What are the DSL Creation Facilities for Clojure and F#? [closed]Clojure 和 F# 的 DSL 创建工具是什么? [关闭]
【发布时间】:2012-06-06 11:13:57
【问题描述】:

我正在尝试确定 Clojure 和 F# 用于创建 DSL 的工具。为了创建和操作 DSL,它们各自提供了哪些工具?

由于 F# 是静态类型的,这是否会使这项特定任务变得更加困难?在 Clojure 部分,我没有真正的经验,但众所周知,所有 LISP 都非常适合元编程/DSL。

我的问题并不是要在两种语言之间进行战争或类似的事情。如果我最近对两者都提出疑问,是因为我确实认为两者都很棒,并且想更多地了解两者的细节。

在阅读了几天有关意向编程的文章后,它让我重新对 DSL 和所有东西产生了兴趣。

虽然我对 F# 有一些了解,但确实我还没有使用引号或类似的东西开发任何东西。我已经看到了基于有区别的联合的 DSL 示例,这些示例看起来很有趣。

【问题讨论】:

  • 这是一个开放式且相当主观的问题。请阅读常见问题解答。

标签: f# clojure metaprogramming dsl


【解决方案1】:

您最终可以用任何语言创建 DSL。

Clojure / 其他 Lisps 特别独特且非常适合元编程的原因在于它们是homoiconic - 也就是说,语言本身以相同语言的数据结构自然地表达。在 Lisp 中,您实际上是直接将代码编写为 AST

这非常强大——这意味着代码生成实际上等同于创建一个相对简单的数据结构。该语言为您提供了在编译时通过宏生成任意代码的工具。这有效地允许您“扩展语言”以支持您需要的任何特定 DSL。

举个例子,我最近发现自己想要一个在 Clojure 中的命令式for 循环(向函数式编程纯粹主义者道歉,但有时你想要一个......)。将其添加到语言中是 5 行:

(defmacro for-loop [[sym init check change :as params] & steps]
  `(loop [~sym ~init value# nil]
     (if ~check
       (let [new-value# (do ~@steps)] (recur ~change new-value#))
       value#)))

所以现在我可以这样做了:

(for-loop [i 0 (< i 10) (inc i)]
   (println i))
=> < prints numbers from 0..9 >

这显然是一个简单的例子,但希望清楚的是,通过创建一组简短的宏来生成新的语言结构,这些宏可以扩展为您想要的代码,这使得构建 DSL 变得特别容易。

一些您可能会感兴趣的阅读/链接:

【讨论】:

  • 非常感谢您的评论。毫无疑问,我稍后会阅读所有这些(格雷厄姆的那本我已经在一段时间前读过;这是一部很好的经典著作)。下面 Alex 提出的一点我忘了提...我说的对吗?
  • Lisp 非常适合嵌入式 DSL,因为正如 mikera 在上面的答案中提到的那样,Lisp 程序是通过使用列表来表达的,然后您可以随意操作这些列表。如果您想编写自定义词法分析器/解析器,则必须使用一些 Lisp 解析器生成器或手动编写自己的解析器。
  • @Jacobo - 编写一个生成 Lisp AST 然后以正常方式从那里继续的解析器很容易。如果您已经在使用 Clojure,我怀疑是否值得引入像 lex / yacc 这样的外部解析器生成器:已经有一堆解析器库可以完成这项工作(例如,受 Haskell 的单子解析器组合器启发的那些)
  • “非常适合元编程”。仅在类似 Lisp 的嵌入式领域特定语言的特定情况下...
  • @JacoboPolavieja 谢谢!关于这个有很多话要说,我写的时候咬着嘴唇。 Homoiconicity 完全没有用,而且可以说是有害多于有益(参见 Mathematica 和 OCaml)。 Lisp 风格的宏有其用途,但它们对 DSL 的实现没有多大帮助,除非您的 DSL 是嵌入式 Lisp 方言。我并不是说 Clojure 会比 F# 差,只是这里声称的优势是虚假的。比较现有的解决方案会很有趣,比如用 Clojure 和 F# 编写的正则表达式编译器...
【解决方案2】:

我不能谈论 Clojure,因为我没有使用过它,但我对 F# 中的 DSL 有所了解。 F# 提供了两个主要的面向语言的编程特性(Don Syme 喜欢这样称呼它们):代码引用和计算表达式。

代码引用更接近于使用 Lisp 等语言中的宏所获得的效果。它们允许您以编程方式生成表达式,然后您可以执行这些表达式。通过在 F# 表达式上使用 ReflectedDefinition 属性,您可以访问它们的 AST。详情请见http://msdn.microsoft.com/en-us/library/dd233212.aspx

计算表达式类似于 Haskell 中的 do 表示法。编译器使用特殊语法将代码重写为对您定义的类型的调用。这种类型应该理想地形成一个单子。由于它们是伪装的 monad,它们应该允许您实现 DSL 的自定义语义。详情请见http://msdn.microsoft.com/en-us/library/dd233182.aspx

IMO 计算表达式更适合在 F# 之上编写 DSL,而代码引用更适合转换或翻译等任务(例如 F# 到 JavaScript)。

除了这两个主要功能之外,您还可以使用其他语言。

当然,上面我只讨论了嵌入式领域特定语言。您可以加倍努力,将 fslex 和 fsyacc 用于独立的 DSL。

【讨论】:

  • 感谢您对这两种技术的评论和愿景。似乎任何 F# 或 Clojure 大放异彩的好东西,你都会发现另一个也同样好。关于嵌入式或外部 DSL 的好点,我忘了提及。通过研究每种语言的特征和做事方式,我真的学到了很多东西。再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-04
  • 2016-01-25
  • 2023-03-20
  • 1970-01-01
相关资源
最近更新 更多