21

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.

  • 4
    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. – Tim Post Jun 3 '11 at 6:36
  • Try to implement something like this in Ruby: meta-alternative.net/pfront.pdf – SK-logic Jun 3 '11 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. – David Thornley Jun 3 '11 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. – OnesimusUnbound Jun 3 '11 at 14:58
  • @OnesimusUnbound: I really would include C++'s templates in your list of language examples, since they are at least theoretically capable of any computation the Lisp macro system can do. As far as Lisp's macros go, get hold of some good Common Lisp code (there's plenty of F/OS Lisp code out there), and search for "defmacro" (you may want to do a case-insensitive search). You'll probably find a whole lot of them. Consider that macros are harder than functions to write and get right, and you'll realize they have to be generally useful. – David Thornley Jun 3 '11 at 15:09
23

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.

  • 1
    "Let Over Lambda" covers much similar ground. Also recommended reading. – John R. Strohm Jun 3 '11 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 . . . – OnesimusUnbound Jun 3 '11 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 Jun 3 '11 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 Jun 3 '11 at 16:38
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.

  • 6
    +1: LOOP doesn't feel very Lispy, as an example of a DSL. – Frank Shearar Jun 3 '11 at 12:49
2

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.)

protected by gnat Jun 5 '17 at 10:06

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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