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