24

One of things that makes Ruby shine is the ability to create Domain Specific Languages better, like

Though one can duplicate these libraries in LISP through macro, I think Ruby's implementation is more elegant. Nonetheless, I think there are cases that LISP's macro can be better than Ruby's, though I could not think of one.

So, in what area is LISP's macro better than Ruby's "ability" to create DSL, if any?

update

I've asked this because modern programming languages are approaching the LISP singularity, like

  • C got macro expansion preprocessor, though very primitive and prone to error
  • C# has attributes, though this is a read-only, exposed through reflection
  • Python added decorator, that can modify the behavior of the function (and class for v 3.0), though feels quite limited.
  • Ruby TMTOWTDI that makes elegant DSL, if care is applied, but in Ruby way.

I was wondering if LISP's macro is only applicable to special cases and that the other programming language features are powerful enough to raise the abstraction to meet the challenges today in software development.

8
  • 5
    I'm not quite sure why there's close votes on this. I though this is the kind of question we want ? Interesting, thought provoking and the scope is defined quite well.
    – user131
    Commented Jun 3, 2011 at 6:36
  • Try to implement something like this in Ruby: meta-alternative.net/pfront.pdf
    – SK-logic
    Commented Jun 3, 2011 at 14:21
  • @Tim Post: One problem is that this really isn't an easy question to answer unless you know both Common Lisp and Ruby well. Another is the fairly ignorant references to other languages, which may annoy purists: there is no language called C/C++, and the significant part of C++ is the template system, and the suggestion that Common Lisp's macro system is only applicable to special cases. Fundamentally, it's a good question, but it's written badly and is hard to answer. Commented Jun 3, 2011 at 14:45
  • @David Thornley, I've updated the post, particularly on C\C++ , and place emphasis on C. As for Common Lisp, I was feeling that since other programming languages have higher abstraction, it's macro feature is for special case. I posted the question, hoping that others will show me that CL's macro isn't for special case is still a powerful feature. Commented Jun 3, 2011 at 14:58
  • 1
    @Tim Post: The original, version of the question was quite argumentative. There was very much a flavor of, "Here is what Ruby does, is there any way in which LISP macros are better?" That has improved substantially after the edits.
    – btilly
    Commented Jun 3, 2011 at 15:38

3 Answers 3

24

Read On Lisp and then decide for yourself.

My summary is that Ruby is better at providing convenient syntax. But Lisp wins, hands down, at the ability to create new abstractions, and then to layer abstraction on abstraction. But you need to see Lisp in practice to understand that point. Hence the book recommend.

6
  • 1
    "Let Over Lambda" covers much similar ground. Also recommended reading. Commented Jun 3, 2011 at 12:32
  • But Lisp wins, hands down, at the ability to create new abstractions, and then to layer abstraction on abstraction. Now I know why . . . Commented Jun 3, 2011 at 14:13
  • It is not a big deal to provide any kind of syntax on top of Lisp. In fact, much easier than with Ruby, thanks to the reader macros.
    – SK-logic
    Commented Jun 3, 2011 at 14:22
  • 1
    @SK-logic: True. However Lispers shy away from reader macros because once you go down that route it no longer feels like a Lisp. In fact some variants of Lisp, such as Clojure, reserve reader macros for the language designer.
    – btilly
    Commented Jun 3, 2011 at 16:38
  • But we are now in 2024 come so far that all this abstractioning is more and more seen as the root of problems. Good luck to try debugging or understanding Lisp Macros (well C++ is on the other hand much worse, a normal programmer even can't understand something simple as std::pair anymore).
    – Lothar
    Commented Feb 1 at 18:11
14

Ruby's facilities for DSL authoring don't change the nature of the language. Ruby's metaprogramming facilities are inherently tied to Ruby syntax and semantics, and whatever you write has to be shunted into Ruby's object model.

Contrast that with Lisp (and Scheme, whose macro facilities differ), where macros operate on the abstract program itself. Because a Lisp program is a Lisp value, a macro is a function mapping one essentially arbitrary syntax to another.

Effectively, a Ruby DSL still feels like Ruby, but a Lisp DSL doesn't have to feel like Lisp.

1
  • 6
    +1: LOOP doesn't feel very Lispy, as an example of a DSL. Commented Jun 3, 2011 at 12:49
6

Ruby's DSLs aren't DSLs at all, and I absolutely hate using them because their documentation flat-out lies about how they really work. Let's take ActiveRecord for example. It allows you to "declare" associations between Models:

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

But the declarativeness of this "DSL" (like the declarativeness of Ruby's class syntax itself) is a horrible lie that can be exposed by anyone who understands how Ruby "DSLs" actually work:

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

(Just try doing anything close to that within the body of a Lisp defclass form!)

As soon as you have code like the above in your codebase, every developer on the project has to fully understand how Ruby DSLs actually work (not just the illusion they create) before they can maintain the code. The available documentation will be completely useless, because they only document the idiomatic usage, which preserves the declarative illusion.

RSpec is even worse than the above, because it has bizarre edge-cases that require extensive reverse-engineering to debug. (I spent a whole day trying to figure out why one of my test cases was being skipped. It turned out that RSpec executes all test cases that have contexts after test cases with no context, regardless of the order in which they appear in the source, because the context method puts your block in a different data structure than it would normally go in.)

Lisp DSLs are implemented by macros, which are little compilers. The DSLs you can create this way are not mere abuses of Lisp's existing syntax. They're actual mini-languages that can be written to be completely seamless, because they can have their own grammar. For example, Lisp's LOOP macro is far more powerful than Ruby's each method.

(I know you've already accepted an answer, but not everybody who reads this is going to want to read the entirety of On Lisp.)

1
  • 1
    Recently "discovered" how DSLs actually work. All the confusion I suffered trying to figure out was going on and it was simply, syntactic sugar coupled with "everything is an object". Kinda enjoy rails more now that I "get it". But it took a while to resolve the cognitive dissonance. Commented Apr 10, 2020 at 3:49

Not the answer you're looking for? Browse other questions tagged or ask your own question.