Victor works on the Angular team at Google. He is interested in functional programming, the Web platform, and client-side applications. Being a language nerd he spends a lot of my time playing with Smalltalk, JS, Dart, Scala, Haskell, Clojure, Ruby, and Ioke. Victor is a DZone MVB and is not an employee of DZone and has posted 46 posts at DZone. You can read more from them at their website. View Full User Profile

Series: DSLs in Groovy, Part: 1

02.16.2011
| 8550 views |
  • submit to reddit

I’ve bought a book about domain specific languages by Martin Fowler recently. It is a great book showing different patterns that can be used when you write a DSL. The author illustrates all the patterns using three languages: Java, C# and Ruby as there is a group of patterns that only applicable to statically typed languages and there are some patterns that require dynamic capabilities to implement them.

One of the nicest features of Groovy is optional typing that allows us to use both kind of patterns. To illustrate how easy it is to write DSLs in Groovy I’m going to write a series of articles showing different patterns.

Though it can be useful to look at some examples in Groovy, to get comprehensive understanding of the patterns it is better to buy Fowler’s book.

Let’s take a look at an extremely simple DSL allowing me to specify who follows whom on Twitter. Firstly, I’m creating Semantic Model for our example:

class User {
private String name
final followers = [] as Set
final following = [] as Set

User(String name){
this.name = name
}

void addFollower(User u){
followers << u
u.following << this
}

boolean equals(obj){
getClass() == obj?.getClass() && name == obj?.name
}
}

Semantic Model contains all required logic and it should be used by other parts of our system. DSL is an additional layer on top of Semantic Model. It helps to to populate it using more readable syntax.

To populate our model directly we can use such a chunk of code:

def jim = new User(‘jim’)
def tom = new User(‘tom’)
jim.addFollower tom

The way I’d like my DSL to look like is:

List<User> users = FollowersGraphBuilder.build {
users 'jim', 'tom', 'jane'
user('jim').follows 'tom'
user('tom').follows 'jane'
}

Let’s start with build method:

class FollowersGraphBuilder {
static List<User> build (Closure c){
def builder = new FollowersGraphBuilder()
c.delegate = builder
c()
builder.createdUsers
}
}

Here we are using Object Scoping to provide all required methods such as users and user to our closure. When I specify delegate property all invocations of ‘users’ method will be delegated to our builder. It allows me to achieve similar behaviour to instance_eval in ruby.

users method just creates a bunch of users and fills a map to refer a user by his name. This pattern is called Symbol Table and it is widely used. Map is the simplest implementation of Symbol Table but it works fine here.

class FollowersGraphBuilder {
private users = [:]

def users(String... userNames){
userNames.each {name->
users[name] = new User(name)
}
}
}

So far our builder can create new users but it still doesn’t know how to specify relationships between them. To make it possible I’m going to create one more builder: UserFollowingBuilder and add a method to FollowersGraphBuilder to create it.

class FollowersGraphBuilder {
def user(String name){
new UserFollowingBuilder(parent: this, user: getUserByName(name))
}

def getUserByName(String name){
assert users.containsKey(name), "Invalid user name $name"
users[name]
}
}

class UserFollowingBuilder {
FollowersGraphBuilder parent
User user

def follows(String name){
parent.getUserByName(name).addFollower user
this
}
}

Now I can write this line of code to specify that john follows piter and bob.

user('john').follows('piter').follows('bob')

The pattern that has been used here is called Method Chaining. It is common when after one of calls the type of the receiver has been changed. It’s happened here when the receiver has been changed from FollowersGraphBuilder to UserFollowingBuilder. To make it look a bit better I am going to add a syntax sugar method and:

class UserFollowingBuilder {
def and(String name){
follows name
}
}

Well, not so bad:

user('john').follows('piter').and('bob')

FollowersGraphBuilder and UserFollowingBuilder are two example of Expression Builder pattern. Their goal is to provide fluent api that basically is the essence of a DSL.

Finally:

class FollowersGraphBuilder {
List<User> getCreatedUsers(){
users.values().toList()
}
}

That is all, I’ve just implemented a simple DSL using a wide variety of patterns:

  • Semantic Model - is our User class; in more complex examples implementing a Semantic Model is the hardest part.
  • Object Scoping - to provide all required methods; can be achieved by inheritance or by specifying delegate property.
  • Symbol Table - to address users by their names.
  • Expression Builder - is the essence of a DSL as it adds so wished fluency to our API.
  • Method Chain - for user('john').follows('piter').and('bob')

Next time I’ll show you how to eliminate all the noise we still have and transform user('john').follows('piter').and('bob') into john.follows(piter).and(bob)

 

From http://vsavkin.tumblr.com/post/3146590777/series-dsls-in-groovy-part-1

Published at DZone with permission of Victor Savkin, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags: