Since I started writing DSLs In Action (buy here)*, a lot of my friends and readers asked me about my decision to make the book polyglotic. Indeed right from the word go, I had decided to treat the topic of DSL based design without a significant bias towards any specific language. Even today after the book has been published, many readers come up to me and ask the same question. I thought I would clarify my points on this subject in this blog post.
A DSL is a vehicle to speak the language of the domain on top of an implementation of a clean domain model. Whatever be the implementation language of the DSL, you need to make it speak the ubiquitous language of the domain. And by language I mean the syntax and the semantics that the experts of the particular domain are habituated to use.
A DSL is a facade of linguistic abstraction on top of your domain's semantic model. As a DSL designer it's your responsibility to make it as expressive to your users as possible. It starts with the mapping of the problem domain to the solution domain artifacts, converging on a common set of vocabulary for all the stakeholders of the implementation and finally getting into the nuts and bolts of how to implement the model and the language.
It's a known fact that there's NO programming language that can express ALL forms of abstraction in the most expressive way. So as a language designer you always have the flexibility to choose the implementation language based on your solution domain model. You make this choice as a compromise of all the forces that come up in any software development project. You have the timeline, the resident expertise within your team and other social factors to consider before you converge to the final set of languages. In short there's always a choice of the language(s) that you make just like any other software development effort.
Being Idiomatic
Same problem domains can be modeled in solution domain using radically different forms of abstraction. It depends on the language that you use and the power of abstraction that it offers. The same set of domain rules may be implemented using the type system of a statically typed language. While you need to use the power of meta-programming to implement the same concepts idiomatically in a dynamically typed language. Even within dynamic languages, idioms vary a lot. Clojure or the Lisp family offers compile time meta-programming in the form of macros to offer your users the specific custom syntactic structures that they need for the domain. While Ruby and Groovy do the same with runtime meta-programming.
Here's an example code snippet from my book of an internal DSL being used to register a security trade and computes its net cash value using the principal amount and the associated tax/fee components. It's implemented in Ruby using all of the Ruby idioms.
str = <<END_OF_STRING
new_trade 'T-12435' for account 'acc-123' to buy 100 shares of 'IBM',
at UnitPrice = 100
END_OF_STRING
TradeDSL.trade str do |t|
CashValueCalculator.new(t).with TaxFee, BrokerCommission do |cv|
t.cash_value = cv.value
t.principal = cv.p
t.tax = cv.t
t.commission = cv.c
end
end
The above DSL has a different geometry than what you would get with the same domain concepts implemented using Clojure .. Have a look at the following snippet of a similar use case implemented in Clojure and executed from the Clojure REPL:
user> (def request {:ref-no "r-123", :account "a-123", :instrument "i-123",
:unit-price 20, :quantity 100})
#'user/request
user> (trade request)
{:ref-no "r-123", :account "a-123", :instrument "i-123", :principal 2000, :tax-fees {}}
user> (with-tax-fee trade
(with-values :tax 12)
(with-values :commission 23))
#'user/trade
user> (trade request)
{:ref-no "r-123", :account "a-123", :instrument "i-123", :principal 2000,
:tax-fees {:commission 460, :tax 240}}
user> (with-tax-fee trade
(with-values :vat 12))
#'user/trade
user> (trade request)
{:ref-no "r-123", :account "a-123", :instrument "i-123", :principal 2000,
:tax-fees {:vat 240, :commission 460, :tax 240}}
user> (net-value (trade request))
2940
The above DSL is implemented using syntactic macros of Clojure for custom syntax building and standard functional programming idioms that the language supports.
In summary, we need to learn multiple languages in order to implement domain models idiomatically in each of them. DSLs In Action discusses all these ideas and contains lots and lots of implementations of real world use cases using Java, Scala, Clojure, Ruby and Groovy.
Hope this clears my intent of a polyglotic treatment of the subject in the book.
* Affiliate Link