Groovy, DSLs and builders

DSLs made easy

The Groovy language is a platform of choice for building DSLs. Using closures, it's quite easy to create custom control structures, as well as it is simple to create builders. Imagine that you have the following code:

email {
	from 'dsl-guru@mycompany.com'
	to 'john.doe@waitaminute.com'
    subject 'The pope has resigned!'
    body {
    	p 'Really, the pope has resigned!'
	}
}

One way of implementing this is using the builder strategy, which implies a method, named email which accepts a closure as an argument. The method may delegate subsequent calls to an object that implements the fromtosubject and body methods. Again, body is a method which accepts a closure as an argument and that uses the builder strategy.

Implementing such a builder is not complicated:

def email(Closure cl) {
    def email = new Email()
    def code = cl.rehydrate(email, this, this)
    code.resolveStrategy = DELEGATE_ONLY
    code()
}

the Email class implements the fromto, ... methods. By calling rehydrate, we're creating a copy of the closure for which we set the delegateowner and thisObject values. Setting the owner and the "this" object is not very important here since we will use the DELEGATE_ONLY strategy which says that the method calls will be resolved only against the delegate of the closure. Then, we're just calling the code and we're done!

Documentation is important

One of the problems with the code that we've shown is that the user of the email method doesn't have any information about the methods that he's allowed to call inside the closure. The only possible information is from the method documentation. There are two issues with this: first of all, documentation is not always written, and if it is, it's not always available (javadoc not downloaded, for example). Second, it doesn't help IDEs. What would be really interesting, here, is for IDEs to help the developper by suggesting, once they are in the closure body, methods that exist on the Email class.

Moreover, if the user calls a method in the closure which is not defined by the Email class, the IDE should at least issue a warning (because it's very likely that it will break at runtime).

Type checking builders

Another problem with the code that we've shown is that it is not compatible with static type checking. If you try to perform type checking on this code:

email {
	from 'dsl-guru@mycompany.com'
	to 'john.doe@waitaminute.com'
    subject 'The pope has resigned!'
    body {
    	p 'Really, the pope has resigned!'
	}
}

Then the type checker will know that there's an email method accepting a closure, but it will complain for every method call inside the closure, because from, for example, is not a method which is defined in the class. Indeed, it's defined in the Email class and it has absolutely no hint to help it knowing that the closure delegate will, at runtime, be of type Email.

@DelegatesTo

For those reasons, Groovy 2.1 introduces a new annotation named @DelegatesTo. The goal of this annotation is to solve both the documentation issue, that will let your IDE know about the expected methods in the closure body, and it will also solve the type checking issue, by giving hints to the compiler about what are the potential receivers of method calls in the closure body.

The idea is to annotate the Closure parameter of the email method:

def email(@DelegatesTo(Email) cl) { ...}

What we've done here is telling the compiler (or the IDE) that when the method will be called with a closure, the delegate of this closure will be set to an object of type Email. But there is still a problem: the defaut delegation strategy is not the one which is used in our method. So we will give more information and tell the compiler (or the IDE) that the delegation strategy is also changed:

def email(@DelegatesTo(strategy=Closure.DELEGATE_ONLY, value=Email) cl) { ... }

Now, both the IDE and the type checker (if you are using @TypeChecked) will be aware of the delegate and the delegation strategy. This is very nice because it will both allow the IDE to provide smart completion, but it will also remove errors at compile time that exist only because the behaviour of the program is normally only known at runtime!

DelegatesTo modes

@DelegatesTo supports multiple modes that we will describe with examples in this section.

Simple delegation

In this mode, the only mandatory parameter is the value which says to which class we delegate calls. Nothing more. We're telling the compiler that the type of the delegate will always be of the type documented by @DelegatesTo (note that it can be a subclass, but if it is, the methods defined by the subclass will not be visible to the type checker).

def build(@DelegatesTo(BuilderItem) cl) { ... }

Delegation strategy

In this mode, you must specify both the delegate class and a delegation strategy. This must be used if the closure will not be called with the default delegation strategy, which is Closure.OWNER_FIRST.

def build(@DelegatesTo(strategy=Closure.DELEGATE_FIRST, value=BuilderItem) { ... }

Delegate to parameter

In this variant, we will tell the compiler that we are delegating to another parameter of the method. Take the following code:

def exec(Object target, Closure code) {
   def clone = code.rehydrate(target, this, this)
   clone()
}

Here, the delegate which will be used is not created inside the exec method. In fact, we take an argument of the method and delegate to it. Usage may look like this:

def email = new Email()
exec(email) {
   from '...'
   to '...'
   send()
}

Each of the method calls are delegated to the email parameter. This is a widely used pattern which is also supported by @DelegatesTo using a companion annotation:

def exec(@DelegatesTo.Target target, @DelegatesTo code) { ... }

A closure is annotated with @DelegatesTo, but this time, without specifying any class. Instead, we're annotating another parameter with @DelegatesTo.Target. The type of the delegate is then determined at compile time. One could think that we are using the parameter type, which in this case is Object but this is not true. Take this code:

class Greeter {
   void sayHello() { println 'Hello' }
}
def greeter = new Greeter()
exec(greeter) {
   sayHello()
}

Remember that this works out of the box without having to annotate with @DelegatesTo. However, to make the IDE aware of the delegate type, or the type checker aware of it, we need to add @DelegatesTo. And in this case, it will now that the greeter variable is of type Greeter, so it will not report errors on the sayHello method even if the exec method doesn't explicitely define the target as of type Greeter. This is a very powerful feature, because it prevents you from writing multiple versions of the same exec method for different receiver types!

In this mode, the @DelegatesTo annotation also supports the strategy parameter that we've described upper.

Multiple closures

In the previous example, the exec method accepted only one closure, but you may have methods that take multiple closures:

def guardWith(Closure guard, Closure code, Closure fallback) { ... }

Then nothing prevents you from annotating each closure with @DelegatesTo:

def guardWith(@DelegatesTo(Foo) Closure guard, @DelegatesTo(Bar) Closure code, @DelegatesTo(Baz) Closure fallback) { ... }

But more importantly, if you have multiple closures and multiple arguments, you can use several targets:

def guardWith(@DelegatesTo.Target('guarded') guarded, 
              @DelegatesTo.Target('codeObject') codeObject, 
              @DelegatesTo.Target('fallbackObject') fallbackObject,  
              @DelegatesTo(target='guarded') Closure guard,
              @DelegatesTo(target='codeObject') Closure code,
              @DelegatesTo(target='fallbackObject') Closure fallback) { ... }

At this point, you may wonder why we don't use the parameter names as references. The reason is that the information (the parameter name) is not always available (it's a debug-only information), so it's a limitation of the JVM.

Statically compiled builders

Leveraging @DelegatesTo

So far, we've only talked about static type checking (@TypeChecked) and IDE completion, but not about static compilation (@CompileStatic). So the question is whether @CompileStatic support it too (and the answer is of course yes!). In this section, we will show you how you can create a type-safe, statically compiled builder leveraging @DelegatesTo. As an example, we will use an HtmlBuilder, which is pretty much like a MarkupBuilder, apart from the fact that we will only allow some tags (for the sake of the example). Also make sure that we are not saying this is the best way to implement such a builder, we're just showing you the capabilities of @DelegatesTo with regards to static compilation. Let's start with the top level class:

import groovy.transform.CompileStatic
 
@CompileStatic
class HTMLBuilder {
   final StringBuilder sb = new StringBuilder()
   String getText() { sb.toString() }
}

The usage would be:

def builder = new HTMLBuilder()
builder.html {
  ...
}

So we miss the "html" method (other properties/methods removed for clarity):

@CompileStatic
class HTMLBuilder {
   
   public static void delegateToTag(Class clazz, Closure body) {
        def tag = clazz.newInstance()
        def clone = body.rehydrate(tag, null, null)
        clone()
   }
 
   void html(@DelegatesTo(HTMLTag) Closure html) {
      delegateToTag(HTMLTag, html)
   }
 
   private static class HTMLTag {}
}

Here, we defined two methods:

We have two problems to solve:

To solve the first issue, we will create an abstract base class for all tags:

@CompileStatic
abstract class Tag {
   protected final StringBuilder sb
   Tag(StringBuilder sb) { this.sb = sb }
   
   void openTag() {}
   void closeTag() {}
   
   public void delegateToTag(Class clazz, Closure body) {
       Tag tag = (Tag) clazz.newInstance(sb)
       def clone = body.rehydrate(tag, this, this)
       tag.openTag()
       clone()
       tag.closeTag()
   }
}

Then our HTMLTag just needs to extends that class:

@InheritConstructors
private static class HTMLTag extends Tag { }

Note that we also moved the delegateToTag method and removed the static modifier, so that we can pass the stringbuilder accross several tag instances, and it now calls openTag before calling the closure, and closeTag after. So now, the main builder looks like this:

@CompileStatic
class HTMLBuilder extends Tag {


   HTMLBuilder() { super(new StringBuilder()) }   

   void html(@DelegatesTo(HTMLTag) Closure html) {
      delegateToTag(HTMLTag, html)
   }
   
   @InheritConstructors
   private static class HTMLTag extends Tag {}
}

The next step is to generate HTML:

   @InheritConstructors
   private static class HTMLTag extends Tag {
       void openTag() { sb.append '<html>' }
       void closeTag() { sb.append '</html>' }
   }

Now what we will do is add a head and a body tag:

   @InheritConstructors
   private static class HTMLTag extends Tag {
       void openTag() { sb.append '<html>' }
       void closeTag() { sb.append '</html>' }
       void body(@DelegatesTo(BodyTag) Closure body) {
           delegateToTag(BodyTag, body)
       }
       void head(@DelegatesTo(HeadTag) Closure head) { delegateToTag(HeadTag, head) }
   }
   @InheritConstructors
   private static class BodyTag extends Tag {
       void openTag() { sb.append '<body>' }
       void closeTag() { sb.append '</body>' }
       
   }
   @InheritConstructors
   private static class HeadTag extends Tag {
       void openTag() { sb.append '<head>' }
       void closeTag() { sb.append '</head>' }
       
   }

Last but not least, let's make the body tag support a p tag:

void p(String body) { sb << "<p>$body</p>" }

Now let's see that we can statically compile the usage too:

@CompileStatic
String test() {
    def builder = new HTMLBuilder()
    builder.html {
       body {
          p 'Hello, static builder!'
       }
    }
    builder.text
}
test()

This prints:

<html><body><p>Hello, static builder!</p></body></html>

But one problem here is that our p tag doesn't support subtags... Can we make it support it too? Sure!

void p(@DelegatesTo(PTag) Closure p) {
   delegateToTag(PTag, p)
}
 
// ...
@InheritConstructors
static class PTag extends Tag {
    void openTag() { sb << "<p>" }
    void closeTag() { sb << "</p>" }
}

And here's how to use it:

builder.html {
       body {
          p 'Hello, static builder!'
          p {
              sb << 'Inside!' // sb works too!
          }
       }
    }

As you can see, here, inside the p tag, we're using sb. This is possible because delegateToTag sets the owner to sb. The static compiler recognizes it and is able to use it, without a single compilation error! So, let's put below the complete example:

import groovy.transform.CompileStatic
import groovy.transform.InheritConstructors
@CompileStatic
abstract class Tag {
   protected final StringBuilder sb
   Tag(StringBuilder sb) { this.sb = sb }
   
   void openTag() {}
   void closeTag() {}
   
   public void delegateToTag(Class clazz, Closure body) {
       Tag tag = (Tag) clazz.newInstance(sb)
       def clone = body.rehydrate(tag, this, this)
       tag.openTag()
       clone()
       tag.closeTag()
   }
}
@CompileStatic
class HTMLBuilder extends Tag {
   
   HTMLBuilder() { super(new StringBuilder()) }
   
   void html(@DelegatesTo(HTMLTag) Closure html) {
      delegateToTag(HTMLTag, html)
   }
   
   String getText() { sb }
   
   @InheritConstructors
   private static class HTMLTag extends Tag {
       void openTag() { sb.append '<html>' }
       void closeTag() { sb.append '</html>' }
       void body(@DelegatesTo(BodyTag) Closure body) {
           delegateToTag(BodyTag, body)
       }
       void head(@DelegatesTo(HeadTag) Closure head) { delegateToTag(HeadTag, head) }
   }
   @InheritConstructors
   private static class BodyTag extends Tag {
       void openTag() { sb.append '<body>' }
       void closeTag() { sb.append '</body>' }
       void p(String body) { sb << body }       
       void p(@DelegatesTo(PTag) Closure p) {
          delegateToTag(PTag, p)
       }
   }
   @InheritConstructors
   private static class HeadTag extends Tag {
       void openTag() { sb.append '<head>' }
       void closeTag() { sb.append '</head>' }      
   }
   @InheritConstructors
   static class PTag extends Tag {
      void openTag() { sb << "<p>" }
      void closeTag() { sb << "</p>" }
   }
}
@CompileStatic
String test() {
    def builder = new HTMLBuilder()
    builder.html {
       body {
          p 'Hello, static builder!'
          p {
              sb << 'Inside!'
          }
       }
    }
    builder.text
}
test()

Congratulations, you've built your first statically compiled builder in Groovy!