We have now reached the point where the term Domain Specific Language is so overused that it has become meaningless. And if not, then we are quickly approaching it. In my mind, this Fall's No Fluff conference will be remembered as "the one about DSL". But we need to slow down... I feel like standing up and shouting, "Not everything is a DSL!" Excel functions? Yes, great example. EasyMock's humane interface? Eh, perhaps. Ordering hashbrowns at Waffle House? umm... maybe this one has questionable value.
It seems that several terms are being confused, and we should be specific about different ideas. A domain specific language is a vocabulary for discussing a specific problem you have. Excel functions are a darn good vocabulary for working with spreadsheet data. They may not be easy to learn, but they are an extremely powerful language to solve the problem of expressing complex formulas in spreadsheets. A fluent interface (or humane interface, or literate interface) is not the same thing as a DSL! A fluent interface is an API design that allows users to work with concepts in a way that closely matches most Western sentence structure, such as:
Compare this API to the traditional Java style API:
receipt = order.2.lattes.andPay($10)
Which API is easier to read, the first or second? Yes, fluent interfaces are easier to read (you answered "first", right?). Hopefully your native language follows the same subject-then-predicate order of English, or else the code example may confuse you, but that's an aside. I do think fluent interfaces are a good idea: they're easy to read and easy to write, despite sometimes being harder to debug because of their multi-step nature.
Order order = new Order();
Receipt receipt = order.pay(new Currency(10, DOLLARS));
But just because something has a fluent interface doesn't mean it is a DSL. EasyMock has a fluent interface, but it is not a DSL. Consider the following unit test code:
This code, if you're at all familiar, creates a mock object and tells it to return 10 when the foo() method is called, and only do it once. The system under test is then exercised and the mock is verified to have been called correctly. This is a fluent interface. The methods are chained together on the 2nd line and it is sort of easy to read.
MyObject mock = new EasyMock.createMock(MyObject.class);
Let's revisit my definition of a DSL: "a vocabulary for discussing a specific problem you have". Is EasyMock a DSL for unit testing? EasyMock's fluent interface provides a nice way to set and verify expectations on mock objects. But setting expectations was not my problem, unit testing collaborating objects was my problem. And the two are different! EasyMock uses a record/playback metaphor to create mocks, and its interface is a DSL for record and playback. Using EasyMock's API in my project didn't solve the problem of writing better unit tests, it created a problem of how to work with record/playback frameworks. Since this fluent interface does not provide a vocabulary for unit testing, it is therefore not a DSL.
So a very important part of a DSL is that it solves the problem you have, not just any problem. Let's consider the famous Waffle House ordering "DSL" of "scattered, covered, and smothered" (these are three different ways to order hashbrowns, by the way). Is this a DSL? What problem does it solve? If you wait tables at Waffle House then it is a terser than English vocabulary used to communicate with the cooks, and therefore a DSL. But if you are a customer at Waffle House, what possible problem does it solve? Can you not say the words "with cheese"? I say ordering at Waffle House is harder than it needs to be because we are forced to adopt the language of the cooks (the implementers). Have you ever ordered a Philly Cheese Steak in Philadelphia? If you don't say it right then you don't get your food. It's nerve wracking for the tourists! Imagine this one... imagine a restaurant where the cooks are Mexican. Can you imagine that? I've just described a ton of places you eat, trust me. Wait if you had to order in Spanish because that is the DSL of the cooks? Well, that's what you're doing at Waffle House. And that is what I'm doing with EasyMock every time I set an expectation.
My preference is to define a DSL in relationship to a person and their particular problem. A language might be a DSL for one person and not another. If you're an API designer, simply saying that you're going to wrap your implementation in a fluent interface is not the same thing as creating a DSL, and you might be exposing the wrong level of abstraction to your users. Creating mini-languages cannot be our goal. We need to first think hard about which problem we need to solve. And providing a nice, fluent interface at just the right level of abstraction can then be a beautiful thing.