« The Computing Disease | Main | Constraining Design in Search of Elegance »

Domain Specific Languages: Objective-C, Ruby and Java (and Groovy)

Listen to this articleListen to this article

I'm forever trying to "improve" my coding and design skills; I say "improve" because there is always the risk that I'm making changes for change sake. One thing I really try hard to do is remove any notion of getting/setting properties of objects and instead focusing on behaviour.

Now I've always tried hard to do this but it's a skill that definitely takes some time and constant practice. TDD certainly has made my life easier by giving me some tools for this but more recently I've really been making an effort to try and "design" my classes in a way that creates Domain Specific Languages (DSL). DSLs enable you to write code that is hopefully more readable and understandable and therefore easier to write, debug and maintain. (At least that's the ide in principle anyway.)

I use a variety of languages in my day-to-day work and I've found that the effort required in creating a DSL to be vastly different depending on the language. Of the few languages I've actually used in anger (Smalltalk unfortunately not being one of them) Objective-C really does provide a nice syntax for creating DSLs (once you get your head around the square brackets).

For example, take the age-old problem of transferring money from one account to another. In Objective-C, assuming we have an Account class with a transfer() method, we can write something like this:

id source = ...;
id destination = ...;

[source transfer:20.00 to:destination]

* Using floats for currency is generally a bad idea but it'll do for illustrative purposes here :-)

Notice the use of named parameters to really help convey the intent. This is actually an optional feature—you can still use positional arguments if you like—but one I use exclusively.

The transfer() method might look like this:

-(void)transfer:(float)amount to:(id)destination {
    [self debit:amount];
    [destination credit:amount];
}

Again, ignoring the unfamiliar method declaration (you'll just have to trust me when I tell you that you do get used to the language and even love it), it's all pretty nice and readable.

Essentially, when calling the method, the identifier that preceeds a colon serves as the name of each argument—ie as used by the caller— with the method name itself serving as the name of the first argument and to for the second.

Once inside the method, the identifier after the colon serves as the name of the argument to be used: amount and destination.

All up, Objective-C is very concise and allows for the creation of fairly nice DSLs without much effort at all. In fact from what I can tell, most Objective-C code turns out to be more-or-less DSL-like; it's typical to see methods calls that look like:

[report printTo:printer withPagination:YES]

The next example I whipped up uses Ruby. Now, I have to admit that I have all of about 3 days of Ruby experience so if you can come up with a better way please, please, please let me know. So, caveats aside, here is the same example in my bestest Ruby, again starting with the usage:

source = ...
destination = ...

source.transfer :amount=>20.00, :to=>destination

This uses a ruby hash—an associative array like a HashMap or Hashtable in Java. You usually need to wrap the hash definition in curly-braces but this can be omitted when the hash is used as the last—or only—parameter.

Now for the method itself:

def transfer params
    amount = params[:amount]
    self.debit amount
    params[:to].credit amount
end

Pretty good really but I still have a preference for the Objective-C use of named parameters. I did read in Programming in Ruby that named parameters was to be a feature of Ruby at some point but hasn't yet made it. I also read somewhere recently that Ruby 1.9 will have a slightly simpler calling syntax so the invocation will look like this:

source.transfer amount:20.00, to:destination

The only Ruby-based DSL I know of is Rake but again my experience with Ruby is somewhat limited.

And finally Java, IMHO the ugliest of the bunch. To achieve something similar in Java seems to me at least (and again, somebody prove me wrong puhlease!) that named parameters with an even remotely useful syntax requires a combination of inner-classes and method chaining. So, to achieve a calling syntax like this:

Account source = ...;
Account destination = ...;

source.transfer(20.00).to(destination);

Requires something at least as complicated as this:

public class Account {
    ...

    public Transfer transfer(float amount) {
        return new Transfer(amount);
    }

    public class Transfer {
        private final float amount;

        Transfer(float amount) {
            this.amount = amount;
        }

        public void to(Account destination) {
            Account.this.debit(amount);
            destination.credit(amount);
        }
    }
}

The best example of a Java-based DSL that I can think of would probably be JMock.

And finally, by popular demand, a Groovy example. Groovy currently supports named parameters for calling methods that accept a Map so the calling syntax is a lot like Objective-C:

def source = ...
def destination = ...

source.transfer(amount:20.00, to:destination)

The transfer() method itself then becomes something like:

void transfer(params) {
    def amount = params.amount
    this.debit(amount)
    params.to.credit(amount)
}

Which looks unsurprsingly like Ruby. Alas, I have no real-world example of a Groovy-based DSL to show you.

TrackBack

Comments

It's really the lack of named parameters that's hurting here, isn't it? Feel free to campaign to get this RFE re-opened: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4124331.

I kind of miss named parameters in Java too. However, here's a way to do something kind of similar in Java:

public static Map toMap(Object ... values) {
Map result = new HashMap();
for (int i = 0; i < values.length; i += 2) {
result.add(values[i], values[i + 1]); // suitable length checking here of course
}
}

This then lets you do, if you want:

destination.transfer(toMap("amount", 20.00, "to", destination));

Hacky, but it kind of works.

You can also use a Command pattern here:

public class Transfer extends Command {
public Transfer to(Account account) { ... ; return this; }
public Transfer from(Account account) { ...; return this; }
public Transfer amount(float amount) { ...; return this; }
public void execute() { from.transfer(amount, to); }
}

Then invoke it like:
new Transfer().amount(20.00).from(firstAccount).to(secondAccount).execute();

With a command pattern, I'd look at generating the commands to avoid writing them. Annotations would be a friend here. Maybe something like this:

public class Account {
...
@command(params = { "amount", "to" })
public void transfer(float amount, Account to) {
...
}
}

Then write something to generate a command called Transfer, that takes an Account in the constructor (to invoke the transfer method on), with a float property called "amount" and an Account property called "to". Saves the scut work, and also helps enforce the "Good Idea(tm)" that Commands should delegate straight down to the domain model.

Yup. Hence my use of the words "at least as complicated..." :-)

Go on, do a groovy example and do us proud Simon !

Wouldn't it be great to see Objective C-on-rails or Smalltalk-on-rails ! Especially with all the nice OSX tools for objective C.

Done ;-)

I'd like to point out that the map based ones are actually very nasty for people not intimately familar with the method being called. Having to know what keys are going to be retrieved is rather icky. Named parameters at least will give you a nice warning: the parameter you tried to assign to does not exist, or something similar.

In any case, complexity is a mixed bag. For example, if you go the generated command route, yes there is complexity setting it up. But that's a one-off cost, and you don't have to worry about it anymore - future effort is on par with the Objective C version. Depends if the cost savings to be made are high enough, really, which in turn is related to how large the project is, and wether you could reuse the generated command infrastructure later.

Personally, I don't find account.transferAmountTo(20.00, destination) to be a big deal. And, unlike ruby, the compiler will tell me if I get the order wrong.

But I still wish Java had named parameters.

The Groovy one looks nice. I like groovy, I like its integration with all the APIs we know and love from java, I like being able to jump into and out of plain old java.

Thanks for wasting an extra 10 mins of your time Simon ! appreciated.

I found it interesting that you didn't cite Rails as an example of a DSL in Ruby. I realise that may well be because you've dodged hype and avoided playing with it, but you've made me think about the relationship between Rails and the DSL concepts.

At the most straightforward level Rails defines a set of different DSLs for various aspects of the problem space. For example, this snip drawn from the Depot application code in the PragProg book shows a DSL for defining relationships between database tables and fields, and another for talking SQL to the database (there are many others):

class Product < ActiveRecord::Base
validates_presence_of :title
validates_presence_of :description
validates_presence_of :image_url
validates_uniqueness_of :title
validates_numericality_of :price
validates_format_of :image_url,
:with => %r{^http:.+\.(gif|jpg|png)$}i,
:message => "must be a URL for a GIF, JPG, or PNG image"

def self.salable_items
find(:all,
:conditions => "date_available <= now()",
:order => "date_available desc")
end


But then there's a whole lot of other less explicitly DSL stuff that to a greater or lesser extent is still domain-specific machinery also implemented within the one Ruby language. You've got some simpler domain-specific but not-so-much-language mark-up in the form of various config and test fixture YAML files; development tools and stubs; code generators; and HTML with embedded code - and with my current lenses in I can see this last one as a DSL with an inverted syntax, where instead of the generic language hosting a DSL, you end up with domain-specific data hosting a generic langauage.

I can argue that Rails takes the "one scripting language to wire them all" flavor of DSLs and reapplies it at the level of a programming framework rather than just that of the programming language.

Hi Anthony,

You're absolutely correct. I hadn't _actully_ thought about Rails, primarily because I haven't played with it...yet. My focus was more on languages that easily support or even encourage the practice; I picked a few examples based on my _actual_ experience. If Ruby had fully-blown named parameters it'd be very close to my language of choice.

I never liked Struts (just ask anyone), gave up on WebWork before I managed to foist it upon an unsuspecting project, haven't used Tapestry, etc. and you can take JSF and, well, you know :)

As for Rails, it looks great, sounds good but ironically, until I hear one of the Rails evangelists write a piece on what they _don't_ like about it and how they got around those difficulties, I'll just take a wait-and-see attitude.

Cheers,

Simon

Post a comment