The Artima Developer Community
Sponsored Link

Weblogs Forum
Creating a Domain Specific Language with Groovy

9 replies on 1 page. Most recent reply: Jun 24, 2010 6:02 PM by Raoul Duke

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 9 replies on 1 page
Jeremy Meyer

Posts: 40
Nickname: jeremy1
Registered: Jan, 2008

Creating a Domain Specific Language with Groovy (View in Weblogs)
Posted: May 8, 2010 2:03 AM
Reply to this message Reply
Summary
An simple example of how easy it is to make a fairly powerful DSL using the dynamic features and powerful sytax of Groovy
Advertisement
DSLs with Groovy

Creating Domain Specific Languages with Groovy

In my last blog I talked about some of the differences between Groovy and Java syntax and argued that anyone making a paradigm shift from Java to dynamic languages and DSLs (domain specific languages) would actually have a fairly easy time of it with Groovy because of its seamless integration with Java. The road to discovering all of Groovy's powerful and "cool" features has the soft grassy verge of Java compatibility. This means that (whilst not ideal) you can experiment and take a bit of a risk with the new language without blowing project deadlines or running into feature deprivation. The worst case scenario is that you can do things in the Java way, until you can refactor to Groovy paradigms.

I also said that Groovy syntax, cool features and operators (like the spread operator) are not necessarily the real reason you should start using Groovy, but the dynamism and the ability to make DSLs is. I want to demonstrate what that dynamism means, and how one might create a tiny DSL (using a Grails-like feature as an example).

To make a DSL useful, we want to end up with a syntax that is functional enough, simple to read and simple to write. Let's look at how Grails does this with a validation DSL in its domain classes. Don't worry if you don't use or know anything about Grails, this is an elegantly simple example and speaks for itself.

class Customer {
   int age
   String name
   static constraints = {
      age(size:18..65)
      name(size:3..20, blank:false)
   }
}
Without thinking about what language it is written in, try and intuit what it might mean. We have a Customer class with two properties, age and name, and some sort of named block, "constraints" with matching age and name listed. Each property name is followed by brackets, inside of which are some descriptions of constraints for those properties. This is very intuitive. It is pretty obvious that the age should be between 18 and 65 and the name should be between 3 and 20 characters and shouldn't be blank. Provided someone gave you a list of allowable constraints (size, blank, zero, date etc.) you would be able to quickly and easily create your own class with defined validation.

But is this Groovy?

The answer is yes and no. It is actually valid Groovy syntax, but as it stands, it is somewhat meaningless and somewhat useless. Right now, syntactically, we have a class definition which has two properties, and a static closure. The closure contains calls to two fictitious methods, age() and name() which both take maps as parameters. What do I mean by fictitious methods? Simply that syntactically we have two method calls, but to methods which don't exist. If we were to run that closure, we would just get the following error:

groovy.lang.MissingMethodException:
No signature of method: Customer$__clinit__closure1.age() is applicable for argument types:
(java.util.LinkedHashMap) values: [[size:18..65]]

By the way, understanding closures is fundamental to using Groovy effectively. There is a lot of information about them available online. If you are unsure about them, a few web searches will put you straight. Otherwise, just think of them as blocks of code which can be passed around in parameters and run when required. I will talk about them a bit more below.

So what good is this phoney code? Well, the syntax looks clean and tidy and has clear affordance, i.e. it shows you intuitively what it should do. Groovy's friendly map syntax (using comma seperated key/value pairs) gives us neat looking parameter lists. This makes it an effective DSL. It is a language-within-a-language which kind of has its own syntax and structure based in the domain of constraining values. That makes programming with constraints very easy; easy to remember, easy to code, easy to read and understand.

But.. it doesn't do anything yet, and since we have a closure with method calls we can't really use, some clever work needs to be done to make this DSL work for us. Let's look at some of the main Groovy mechanisms we need.

Method Interception

One of the dynamic features of Groovy is that it can intercept methods. The simplest way to do this is to override the invokeMethod method on your class. In its simplest usage, every time you call a non-existent method on your object, invokeMethod will be called first, and passed the method name and the parameter list. The example below just prints the name of the method being called.

class Worker {
   Object invokeMethod(String name, Object args) {
      println “${name} method called.”
   }
   ..
} 
Using this simple technique, if a method actually does exist, that method will be called, and the invokeMethod will not. See the GroovyInterceptable interface for details of how to implement more complex behaviour.

Dynamic Invocation

As a corollary to Method Interception, Groovy can call methods and get and set properties dynamically. You can call a method by calling the invokeMethod method directly, and you can set and get properties by using setProperty, as in clown.setProperty(“nose”, “red”), the dynamic equivalent of clown.setNose(“red”). Doing this in a language like Java would require much more code.

Closures and Delegates

Since a closure is a defined block of code that is not actually run at the time it is defined, it needs to be able to keep track of its definition-time state. The Groovy mechanism for this is the delegate. When you define a closure, it keeps a reference to this, its “birthday” context in its delegate property. A method call in a closure, will resolve to the closure's delegate by default. If you want your closure to call a method on an object other than the one which defined it, you have to set the delegate of your closure to point to that object.

Putting them together


Lets look at the code for a simple validator:
class Validator {
def subject
public void validate(def o) {
subject = o
o.constraints.setDelegate(this)
o.constraints.call()
println "Validation complete."
}


Object invokeMethod(String name, Object args) {
def val = subject.getProperty(name)
args[0].each {
switch(val?.class) {
case null: if (it.key =="blank" && !val)
println "failed: property '${name}' is null."
break
case Integer :
if (it.key == "size" && !(it.value.contains(val)))
println "failed: Integer property '${name}' has value '${val}' not in range '${it.value.inspect()}'."
break
case String: if (it.key =="size" && !it.value?.contains(val.length()))
println "failed: String property '${name}' has value '${val}' not in length range '${it.value.inspect()}'."
break
default:
break
}
}

}
}

The validate method references the object's constraints closure, sets the delegate of the closure to point to itself and then runs the closure. This is where the interesting stuff happens. The closure calls the two methods "age" and "name". These are called on the validator object (which is the new delegate of the closure). Since the methods do not exist on the validator object, the invokeMethod handler is called, which is where the validation actually takes place.

In invokeMethod, we can find out from the parameters the name of the method that was attempting to be called and get the value of the property with that name, in the line:

def val = subject.getProperty(name)

This is where the "language" comes into play. We are using bogus method names to point us to properties which need validating. We can use Groovy's default exception behaviour here to handle the case where the method name in the constraint closure doesn't match a property name. If the property is not found, Groovy will throw a groovy.lang.MissingPropertyException. Once we have the property value, we look at the other arguments, the maps, which were passed to our bogus method call. They contain the key/value pairs "size:" and "blank:". We can make good use of Groovy's range syntax here for easy validation:

it.value.contains(val)
In the switch statement we can check the actual property value for its type and so allow for different validation behaviour (i.e. size for a String means something different from size for an Integer). Note that we can use the safe navigation operator, "?." to ensure that if the value is null, we will catch it in the switch statement in the null case, rather than throw a null pointer exception:

switch(val?.class)

That's It!


About 30 lines of code later, we have a (somewhat primitive) validator, but one which allows us to use a Domain Specific Language approach to define our constraints. Groovy's closures, powerful operators and syntax and its easy introspection have made it a fairly simple task.
To use our validator, we create a Customer, create a Validator and call the validate method. Note that you are more likely to want a static validation method on the Validator class if you want to use this for production code, but this demonstrates the point.

c = new Customer(age: 2, name:null)
v = new Validator()
v.validate(c)
If we run this we get:
size validation failed: Integer property 'age' has value '2' not in range '18..65'.
blank validation failed: property 'name' is null.
Validation complete.
Simple!

References

http://groovy.codehaus.org/Writing+Domain-Specific+Languages
http://www.martinfowler.com/bliki/DomainSpecificLanguage.html


Michael Stover

Posts: 28
Nickname: mstover
Registered: Jul, 2005

Re: Creating a Domain Specific Language with Groovy Posted: May 8, 2010 5:22 AM
Reply to this message Reply
Does that really make it a DSL? You are using Groovy syntax and capabilities, not designing a new syntax and set of capabilities to do the job. It's not a "language", more like a design pattern in groovy.

Fred Finkelstein

Posts: 48
Nickname: marsilya
Registered: Jun, 2008

Re: Creating a Domain Specific Language with Groovy Posted: May 10, 2010 5:23 AM
Reply to this message Reply
>> class Customer {
>> int age
>> String name
>> static constraints = {
>> age(size:18..65)
>> name(size:3..20, blank:false)
>> }
>>}

It's soooo lovely, you can read this as if it were prose.

Scott McKinney

Posts: 3
Nickname: smckinney
Registered: May, 2010

Re: Creating a Domain Specific Language with Groovy Posted: May 13, 2010 8:09 PM
Reply to this message Reply
Interesting and clever use of the closure delegate!

There's a couple of issues I have with the approach, however. First, the constraints are not in proximity to the target features, making it difficult to match up and read the code in terms of understanding what exactly is constrained. Second, by using dynamic method handling we give up type-safety and desing-time feedback. For instance, my editor can't help me complete a constraint nor can it tell me if I've entered an invalid one.

Maybe a better approach could involve annotations e.g.,

class Customer {
@constraint( size:18..65 )
int age
@constraint( size:3..20, blank:false )
String name
}

Jeremy Meyer

Posts: 40
Nickname: jeremy1
Registered: Jan, 2008

Re: Creating a Domain Specific Language with Groovy Posted: May 13, 2010 11:04 PM
Reply to this message Reply
> however. First, the constraints are not in proximity to
> the target features, making it difficult to match up and
> read the code in terms of understanding what exactly is
> constrained.

I see your point, but actually, I find annotations ugly and difficult to read. It is much more elegant to have a code block which handles all of your constraints. And they match perfectly by name. I think the creators of Grails probably went with the DSL approach precisely to avoid the somewhat messy interspersal of code with annotation.

> give up type-safety and desing-time feedback.

re. type safety. One of the tenets of dynamic languages is that developers have over-emphasised the importance of type safety. There are many, many debates in the literature about this, so I won't discuss here.

> instance, my editor can't help me complete a constraint
> nor can it tell me if I've entered an invalid one.

It shouldn't take more than 10 minutes to write a macro in almost any editor to create a constraint block from your code. Even if you can't yet, that is not a problem in principle, it is just that editors have not caught up yet.

Code editors were very primitive when they first came out, but they have evolved constantly, always a few steps behind the language. Code sense type behaviour has already vastly improved in a lot of dynamic language editors and with the right extensibility APIs, it would be fairly easy for developers delivering DSL approaches to create the editor tools to go with them.

Scott McKinney

Posts: 3
Nickname: smckinney
Registered: May, 2010

Re: Creating a Domain Specific Language with Groovy Posted: May 14, 2010 10:27 AM
Reply to this message Reply
I suppose I don't understand what is "ugly" about:

@constraint( size:18..65 )
int age

I'll agree that sometimes annotations are overused, but in this case it sure seems made-to-order.

I also understand the notion that strict type-safety is an overplayed concept. Sometimes it's too burdensome to stay withing static boundaries to get the job done with respect to time and maintainability, esp. meta programming. So I do, in part, appreciate Groovy's dynamic features. Unfortunately, from my perspective, these features are abused more often than not.

The real benefit Groovy offers as a static language is not so much type-safety from compile-time validation, although that is still very high on the list. Today we have vast computing power at our disposal. Editors can perform a staggering amount of static analysis between keystrokes giving us instant feedback and code completion. Static language parsers can achieve most of the benefits of dynamic languages via type inference. Groovy and emerging tools for it strike a near perfect balance in this regard e.g., IntelliJ IDEA's Groovy support.

But if we use Groovy's dynamic features we escape the watchful eyes of our editor and other tools. We lose code completion and instant feedback; we're programming in the dark. Sometimes it's worth the tradeoff (meta programming), but in a lot of cases it's not.

Back to your example. What exactly have we gained over the annotation approach? It's certainly no more readable and it's about the same amount of code. What have we given up? Of course we lose build-time validation. And we've lost design-time feedback -- the editor can no longer tell me what my options are e.g., size, blank, etc. I now have to execute code to validate it syntactically. We've also lost proximity; it's harder to visually inspect if we've missed a constraint. Also, as with most DSL solutions, we've lost familiarity. By inventing something new we've added to the learning curve.

So it seems in this case the dynamic solution isn't quite worth it. At least not to me.

Scott McKinney

Posts: 3
Nickname: smckinney
Registered: May, 2010

Re: Creating a Domain Specific Language with Groovy Posted: May 14, 2010 3:33 PM
Reply to this message Reply
Well I take it back... the bit about a "near perfect balance."

I'm not a Groovy programmer. In fact I've never used it at all, just read bits about it here and there and assumed it was statically typed. To not be a total blow hole I went and read up a bit more about it and wouldn't you know, it's not statically typed -- at all. It just looks that way. Probably a better description of Groovy's type system would be dynamically typed with optional type annotations.

If only Groovy were truly "optionally statically typed" as I've read in other places...

Morgan Conrad

Posts: 307
Nickname: miata71
Registered: Mar, 2006

Re: Creating a Domain Specific Language with Groovy Posted: May 14, 2010 4:30 PM
Reply to this message Reply
The interceptor/closure technique is very neat. Cool. The actual application for data validation is, to my mind, more of an excuse to show the interceptor technique that a real revelation.

Yes, it's an interesting way to add constraints, but, IMHO, annotations are clearer, and, frankly, simply checking values in the constructor (or setters if you are using them) and throwing exceptions, combined with good comments/javadocs in the code, is just as good.

In the long run, whichever technique is easier to read and maintain should win out. It does require discipline to write in the javadocs that a value will never be null, must be a positive number, etc... And keeping them in synch with the code is tough. So I'm eager for some improvements there.

Minor snit - I don't like the example's use of "size" to mean both the range of an integer, and the length of a String. And the mathematician in me wants to see '[' and '(' options for the bounds for inclusive/exclusive.


I'm expecting Robert to pipe in here with something about database constraints, cause in many cases they are the underlying driving force. How can they best be reflected in the code and easily understood by programmers?

Jeremy Meyer

Posts: 40
Nickname: jeremy1
Registered: Jan, 2008

Re: Creating a Domain Specific Language with Groovy Posted: May 15, 2010 6:07 AM
Reply to this message Reply
> I suppose I don't understand what is "ugly" about:
>

> @constraint( size:18..65 )
> int age
>

Probably nothing, but that was not my point. If you had 20 - 30 such attributes, you would have the word @constraint 20 - 30 times, one in between each attribute. Surely you can't think that looks good?

> I also understand the notion that strict type-safety is an
> overplayed concept.
As I said, not a debate for here. Type Safety / Static vs. Dynamic typing has been done to death, it has become a religious argument.

> Back to your example. What exactly have we gained over the
> annotation approach?

It seems I didn't emphasise in the blog that I did not create this idea, it is the approach that Grails uses when defining entity classes and is commonly accepted as a DSL. I merely reverse engineered the DSL to show how one might have implemented it, to showcase some of the dynamic power of Groovy. I assume the creators of Grails did something similar. BTW, annotations fit the definition of Domain Specific Languages.

Raoul Duke

Posts: 127
Nickname: raoulduke
Registered: Apr, 2006

Re: Creating a Domain Specific Language with Groovy Posted: Jun 24, 2010 6:02 PM
Reply to this message Reply
> If only Groovy were truly "optionally statically typed" as
> I've read in other places...

while not exactly that, on the whole i'd (just personally mind you) rather do dsls in scala. i use groovy in my day job and its particular dynamism makes my development cycle longer and more painful than it should be, imhumbleo.

Flat View: This topic has 9 replies on 1 page
Topic: Java 3D Fun with Groovy Previous Topic   Next Topic Topic: Traits for Java

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use