The concept of a DSL is actually unrelated to Ruby, and has been around for a long time. It's generally used to describe a language that has constructs suited for expressing concepts of a particular domain. The build tool Make, for instance, has a language that allows to declaratively define build targets, how to build them and dependencies between them. Then there is the concept of an internal DSL, which uses the syntax of an exіsting language, a host language, such as Ruby. The means of the language are used to build constructs resembling a distinct language. The, already mentioned, Rake uses this to make code like this possible:
task :codeGen doAs can be seen, the host language must be quite flexible to allow the DSL to have a distinct look.
# do the code generation
end
Recently, however, the term DSL seems to have become overused, causing it to become vague. It seems that omitting parentheses from function calls already qualifies a piece of code to be labeled a DSL. O'Reilly blogger chromatic came up with a tongue-in-cheek checklist for deciding if a piece of code is a DSL, or at least when it's labeled as one. Here a sample:
1. Have you ever programmed in a language other than Ruby? (PHP and HTML don’t count.) If not, it’s a DSL.#5 on chromatic's list is particularly telling:
2. Is the defining syntactic feature that you’ve cleverly left the parentheses off of a list of function arguments? If so, it’s a DSL.
3. Is the code primarily a list of key-value pairs? Welcome to DSL Town, population you!
5. Have you ever used the phrase “… and it reads just like English!” in seriousness? You’d better get to the hospital; you’re coming down with a case of the DSLs!This is a popular argument for creating DSLs: they allow to write code that is easy to read and understand and doesn't hide its meaning behind language constructs such as for loops, conditionals, etċ. However, David A. Black offers another view of this:
This:Using a domain specific vocabulary for naming code elements isn't a new concept, instead it should be an obvious choice for a designer.
with Employee "123-45-6789" do
dock_salary 1000
warn_about :misconduct
end
is not a domain specific language. It is, rather, domain specific language. Note the lack of the “a”. It’s domain specific, and it uses language-like constructs. But it isn’t a language. It’s Ruby, using expressive method names and cushy semantics and therefore facilitating the use of domain specific idioms.
So, is the term DSL bad or misleading? Not necessarily. Smalltalk developer Blaine Buxton shares his view:
I've always wondered what the big deal with DSLs was. Now, i'm not saying they are bad quite the contrary.But, I believe a DSL is a healthy bi-product of a good object-oriented design. So, I was a little annoyed with all of the talk and the tricks. I kept thingking to myself, "But, if you did a good design, you would have this!"Languages with syntax that can be twisted/bent to allow for more concise code are useful, but good design still depends on the developer.
But, I should really be kicking myself. I should be glad that good design is back in vogue. and you know what? I am. I'm not annoyed any long and I relish all of this new talk on DSLs. I even feel guilty. You see, DSLs have always been part of the Smalltalk farbic. It's natural to us because it's the way we have learned to code. I remember having the mantra "Code should read like a conversation" shoved down my throat until it became second nature. The cool thing is that there is a whole new generation finding out about this and doing it.
There is another side to this. Some DSLs make use of Ruby constructs and corner cases of Ruby syntax. While this allows the developer to achieve a certain look, this doesn't mean that this is a good design. After all, the fact that code is written once, but read and modified much more often, is certainly still true.
A recent article on Urban Honking started of a debate about this. The article explains how the HTML parser library Hpricot makes this code possible:
Hpricot(my_document)Basically, there's a function called Hpricot which creates a new object of the class Hpricot and parses my_document. Smalltalk blogger James Robertson, however, takes issue with that:
Urban Honking goes to great pains to explain why using syntax tricks in Ruby to get to something like this: Hpricot(my_document) Is a good thing. Here's a question - if you stumbled on that in code, would you have any idea what it did?After explaining how to improve the code, he continues:
I've out-clevered myself in Smalltalk many times; it's never a good idea.This view is also backed by Stuart Halloway:
If you are going to bend a language's idiomatic usage, you should have a compelling reason, and I share James' view that this example is not compelling.He goes on to give an interesting overview of the many different solutions to API design, and how to design an API to make it possible to write expressive code with it.
What is your opinion? Where do you draw the line between good API design, that makes code easy to read, and using tricks that save a few key strokes but make code maintenance difficult? Are there ways to check that you've strayed from good design into the realm of hacking and clever syntax tricks?