LISP的宏在哪些方面比Ruby的创建DSL的“能力”更好?


21

使Ruby发光的一件事是能够更好地创建特定于域的语言,例如

尽管可以通过宏在LISP中复制这些库,但我认为Ruby的实现更为优雅。尽管如此,我认为在某些情况下LISP的宏可以比Ruby的宏更好,尽管我想不到。

那么,LISP的宏在哪一个领域比Ruby创建DSL的“能力”(如果有)更好?

更新

我之所以这样问,是因为现代编程语言正在接近LISP的奇点,例如

  • C获得了宏扩展预处理器,尽管非常原始并且容易出错
  • C#具有属性(尽管这是只读的),通过反射公开
  • Python增加了装饰器,尽管感觉很有限,但它可以修改函数的行为(以及v 3.0的类)。
  • Ruby TMTOWTDI,如果应用得当,则可以使用Ruby方式制作精美的DSL。

我想知道LISP的宏是否仅适用于特殊情况,而其他编程语言功能是否强大到足以提高抽象度来应对当今软件开发中的挑战。


4
我不太确定为什么要对此进行密切投票。我虽然这是我们想要的问题?有趣,发人深省的范围很明确。
蒂姆·波斯特

尝试在Ruby中实现这样的功能:meta-alternative.net/pfront.pdf
SK-logic

@Tim Post:一个问题是,除非您对Common Lisp和Ruby都非常了解,否则这确实不是一个容易回答的问题。另一个是对其他语言的相当无知的引用,这可能会惹恼纯粹主义者:没有称为C / C ++的语言,而C ++的重要组成部分是模板系统,而​​有关Common Lisp的宏系统仅适用于特殊情况的建议。从根本上讲,这是一个很好的问题,但是写得不好,很难回答。
David Thornley

@David Thornley,我已经更新了这篇文章,特别是在C \ C ++上,并且重点放在了C上。至于Common Lisp,我感觉到由于其他编程语言具有更高的抽象性,因此它的宏功能适用于特殊情况。我发布了问题,希望其他人可以向我展示CL的宏不适用于特殊情况,这仍然是一个强大的功能。
OnesimusUnbound,2011年

@OnesimusUnbound:我确实会将C ++的模板包括在您的语言示例列表中,因为从理论上讲,它们至少能够进行Lisp宏系统可以执行的任何计算。就Lisp的宏而言,掌握一些良好的Common Lisp代码(那里有很多F / OS Lisp代码),然后搜索“ defmacro”(您可能希望执行不区分大小写的搜索)。您可能会发现很多。考虑到宏比编写和编写函数要难,而且您会意识到它们通常必须有用。
David Thornley

Answers:


23

阅读Lisp,然后自己决定。

我的总结是,Ruby更好地提供了方便的语法。但是Lisp能够创造新的抽象,然后在抽象上分层抽象,这是他获胜的机会。但是您需要在实践中了解Lisp才能理解这一点。因此,本书推荐。


1
“放任Lambda”涵盖了许多相似的方面。还建议阅读。
John R. Strohm

But Lisp wins, hands down, at the ability to create new abstractions, and then to layer abstraction on abstraction.现在我知道为什么了。。。
OnesimusUnbound,2011年

在Lisp之上提供任何语法都没什么大不了的。实际上,感谢读者宏,这比使用Ruby容易得多。
SK-logic

1
@ SK-logic:是的。但是,Lispers避开了读取器宏,因为一旦您沿着那条路线走,它就不再像Lisp。实际上,Lisp的某些变体(例如Clojure)为语言设计人员保留了阅读器宏。
btilly 2011年

14

Ruby的DSL创作功能不会改变语言的性质。Ruby的元编程功能与Ruby语法和语义固有地联系在一起,无论您编写什么内容,都必须将其分入Ruby的对象模型中。

与Lisp(和Scheme,其宏设施有所不同)相反,Lisp 在抽象程序本身上运行。由于Lisp程序 Lisp值,因此宏是将一种基本任意语法映射到另一种语法的函数。

实际上,Ruby DSL仍然感觉像Ruby,但是Lisp DSL不必感觉像Lisp。


6
+1:作为DSL的示例,LOOP感觉不太轻松。
Frank Shearar 2011年

2

Ruby的DSL根本不是DSL,而且我绝对不喜欢使用它们,因为它们的文档内容完全取决于它们的实际工作方式。让我们以ActiveRecord为例。它允许您“声明”模型之间的关联:

class Foo < ActiveRecord::Base
    has_one :bar
    has_one :baz
end

但是,这种“ DSL”的声明性(如Ruby class语法本身的声明性)是一个可怕的谎言,任何了解Ruby“ DSLs”实际工作原理的人都可以揭露:

class Foo < ActiveRecord::Base
    [:bar,:baz,:qux,:quux].each do |table|
        has_one table if i_feel_like_it?(table)
    end
    puts "Just for shits and giggles, and to show"
    puts "just how fucked up Ruby really is, we're gonna ask you"
    puts "which SQL table you want the Foo model to have an"
    puts "association with.\n"
    puts "Type the name of a table here: "
    has_one gets.chomp.to_sym
end

(只要尝试在Lisp 表单的主体内执行任何接近该动作的动作即可defclass!)

一旦您在代码库中有了上述代码,项目中的每个开发人员都必须完全理解Ruby DSL的实际工作方式(而不仅仅是他们创建的错觉),然后才能维护代码。可用的文档将完全无用,因为它们仅记录了惯用用法,从而保留了声明性的幻觉。

RSpec甚至比上述还要差,因为RSpec具有奇怪的边缘情况,需要进行大量的逆向工程才能进行调试。(我花了整整一天的时间来弄清楚为什么我的一个测试用例被跳过了。事实证明,RSpec 没有上下文的情况下执行所有具有上下文的测试用例,而不管它们在源中出现的顺序如何。 ,因为该context方法会将您的块置于与通常使用的块不同的数据结构中。)

Lisp DSL由宏实现,这些宏是很少编译器。您可以通过这种方式创建的DSL不仅仅是滥用Lisp的现有语法。它们是实际的迷你语言,可以编写为完全无缝的,因为它们可以具有自己的语法。例如,Lisp的LOOP宏比Ruby的each方法强大得多。

(我知道您已经接受了答案,但并不是每个读此书的人都希望阅读On Lisp的全部内容。)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.