Over a million developers have joined DZone.
Platinum Partner

Speak To Your Domain

· Java Zone

The Java Zone is brought to you in partnership with ZeroTurnaround. Discover how you can skip the build and redeploy process by using JRebel by ZeroTurnaround.

Domain Specific Languages (DSL) have been gaining popularity steadily for quite some time. We'll jump right to some little experiments, but those who'd prefer to get sound theoretical background, please check out the Martin Fowler's article or wikipedia.

It's all about money

Here I'm going to play around with simple and intuitive examples of internal DSLs defined in Groovy. First, check out this little piece of Java code:

Money m1=new Money(10, "EUR");
Money m2=new Money(10, "USD");
Money m3=m1.add(m2.to("EUR")).to("GBP");

How quickly can you get what the code does? Two instances of the Money class with different currencies are created. After converting m2 into EUR the two instances are added and then the result is converted into GBP currency. It wasn't that difficult, was it? And how about the next piece, does it do the same thing?

Money m3=new Money(10, "EUR").add(new Money(10, "USD")).to("EUR").to("GBP");

The correct answer is it doesn't. You'll get a runtime exception for adding money instances with different currencies. Note also that even though we're using a statically typed language here, the compiler cannot check violations of our business logic (adding different currencies) for us.
Let's try again. Is the following piece of code doing the same thing as the original example?

Money m3=new Money(10, "EUR").add(new Money(10, "USD").to("EUR")).to("GBP");

Yes, now it is correct, but I definitely need some time to make sure I understand the code and get all the parentheses right.
Now let's try the same thing, but using a simple Groovy-based DSL.

Money m1=10.eurMoney 
m3=(m1 + m2.eur).gbp

How difficult was it to understand the code? In my opinion, it was at least a bit easier then in the Java case, but I've been trained to read Groovy code, which makes me not very objective. I'd be very interested in other people's opinion.
Now, let's try with the one-line examples. First the one with error of adding money with different currencies. Can you spot the error easily?

Money m3=(10.eur + 10.usd).eur.gbp

The reduced number of parentheses together with property-based syntax makes the code structure more obvious.
And now the correct variant:

Money m3=(10.eur + 10.usd.eur).gbp

Note that following a couple of simple principles makes a general-purpose language, like Groovy, suitable for internal DSLs. These are in particular loose syntax regarding parentheses, semicolons and the return keyword, operator overloading and direct property access. Meta-programming also helps a lot by enabling us to add extra methods or properties at runtime.
Another useful concept are named parameters, which let you specify any combination of values for properties at object creation time, without having to define constructors for all the possible variants. So code like this:

process(new Order(new Modey(167, "USD"), new Money(19, "USD"), null, 0), true)

can be replaced with this:

process(new Order(netPrice:167.usd, tax:19.usd), true)

Sending money is easy, receiving them is fun

And finally a more involved concrete example from an accounting application. To transfer money from one account to another, you typically need to write code like this:

Money money = new Money(amount: 10, currency: 'eur')
getAccount('Account1').withDraw money
getAccount('Account3').deposit money

The code must run within a transaction, typically as part of a transactional service, and should be also protected with security constraints. If transferring money between accounts is very frequent in your application, why not to introduce a DSL, which would make the code shorter and more readable? The same money transfer could be expressed as:

"Account1" >> 10.eur >> "Account3"

This is still valid Groovy code and can be put anywhere in your application, so pretty easily the code can be made secure and transactional. As being valid Groovy code, you can use your existing IDE and the build environment to edit, test, debug and refactor it. The code might be quickly turned into something like:

retrieveSourceAccountNumber(order.customer) >> order.totalPrice >> getOurPaymentAccount()

And of course the DSL can be also combined with control logic or iterations:

["Account1", "Account2"].each{it >> 10.eur >> "Account3"}


employees.each{"Our_Company_Account" >> 1000000.eur >> it.salaryAccount}

The presented DSL builds on the ability of numbers to express money through dynamic properties, plus the ability of the right shift operator on String to retrieve the account by identifier and perform the actual money transfer. These capabilities are, however, not available by default. We have to add them, which means we have to create a DSL.
Defining such a DSL isn't much work. We only need to teach numbers to work with currencies and Strings to deal with accounts and money transfers. I found the Groovy concept of categories very handy for this purpose.
For example, adding currency-related properties to numbers is as simple as defining a single Java or Groovy class.

class MoneyCategory {
    static Money getEur(Number num) {new Money(amount:num, currency:"eur")}
    static Money getUsd(Number num) {new Money(amount:num, currency:"usd")}
    static Money getCzk(Number num) {new Money(amount:num, currency:"czk")}
    static Money getGbp(Number num) {new Money(amount:num, currency:"gbp")}

    static Money getEur(Money money) {money.to("eur")}
    static Money getUsd(Money money) {money.to("usd")}
    static Money getCzk(Money money) {money.to("czk")}
    static Money getGbp(Money money) {money.to("gbp")}

And now within use blocks you can use the properties like in this example:

use (MoneyCategory) {

With DSLs in my toolbox I can see new horizons ahead. And they are fantastic.

From http://www.jroller.com/vaclav

The Java Zone is brought to you in partnership with ZeroTurnaround. Discover how you can skip the build and redeploy process by using JRebel by ZeroTurnaround.


{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}