Agile Zone is brought to you in partnership with:

Debasish specializes in leading delivery of enterprise scale solutions for various clients ranging from small ones to Fortune 500 companies. He is the technology evangelist of Anshin Software (http://www.anshinsoft.com) and takes pride in institutionalizing best practices in software design and programming. He loves to program in Java, Ruby, Erlang and Scala and has been trying desperately to get out of the unmanaged world of C++. Debasish is a DZone MVB and is not an employee of DZone and has posted 55 posts at DZone. You can read more from them at their website. View Full User Profile

DSL Interoperability and Language Cacophony

04.26.2010
| 7570 views |
  • submit to reddit
Many times I hear people say that DSL based development often leads to a situation where you have to manage a mix of code written in various languages, which are barely interoperable and often need to be integrated using glues like ScriptEngine that makes your life more difficult than easier. Well, if you are in this situation, you are in the world of language cacophony. You have somehow managed to pitchfork yourself into this situation through immature decisions of either selecting an improper language of implementation for your DSL or the means of integrating them with your core application.

Internal DSLs are nothing but a disciplined way of designing APis which speak the Ubiquitous Language of the domain you are modeling. Hence when you design an internal DSL, stick to the idioms and best practices that your host language offers. For domain rules that you think could be better modeled using a more expressive language, select one that has *good* interoperability with your host language. If you cannot find one, work within the limitations of your host language It's better to be slightly limiting in syntax and flexibility with your DSL than trying to force integration with a language that does not interoperate seamlessly with your host language. Otherwise you will end up having problems not only developing the interfaces between the DSL and the host language, you will have other endless problems of interoperability in the face of exceptions as well.

Don't let the language cacophony invade the horizons of your DSL implementation!

Consider the following example where we have a DSL in Groovy that models an Order that a client places to a securities broker firm for trading of stocks or bonds. The DSL is used to populate orders into the database when a client calls up the broker and asks for buy/sell transactions.
newOrder.to.buy(100.shares.of('IBM')) {
limitPrice 300
allOrNone true
valueAs {qty, unitPrice -> qty * unitPrice - 500}
}
I am not going into the details of implementation of this DSL. But consider that the above script on execution returns an instance of Order, which is an abstraction that you developed in Groovy. Now you have your main application written in Java where you have the complete domain model of the order processing component. Your core application Order abstraction may be different from what you have in the DSL. Your DSL only constructs the abstraction that you need to populate the order details that the user receives from their clients.

It's extremely important that the Order abstraction that you pass from executing the Groovy script be available within your Java code. You can use the following mechanism to execute your Groovy code ..
def dslDef = new File('ClientOrder.groovy').text
def dsl = new File('order.dsl').text
def script = """
${dslDef}
${dsl}
"""
new GroovyShell().evaluate(script)

This runs the script but does not have any integration with your Java application. Another option may be using the Java 6 ScriptEngine for talking back to your Java application. Something like the following snippet which you can have wiothin your main application to execute the Groovy script using ScriptEngine ..
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("groovy");

List<?> orders = (List<?>)
engine.eval(new InputStreamReader(
new BufferedInputStream(
new SequenceInputStream(
new FileInputStream("ClientOrder.groovy"),
new FileInputStream("order.dsl")))));

System.out.println(orders.size());
for(Object o : orders) {
System.out.println(o);
}
Here you have some form of integration, but it's not ideal. You get back some objects into your Java code, can iterate over the collection .. still the objects that you get back from Groovy are opaque at best. Also since the script executes in the sandbox of the ScriptEngine, in case of any exception, the line numbers mentioned in the stack trace will not match the line number of the source file. This can lead to difficulty in debugging exceptions thrown from the DSL script.

Groovy has excellent interoperability with Java even at the scripting level. When integrating DSLs with your main application always prefer the language specific integration features over any generic script engine based support. Have a look at the following that does the same job of executing the Groovy script from within a Java application. But this time we use GroovyClassLoader, a ClassLoader that extends URLClassLoader and can load Groovy classes from within your Java application ..
public class RunScript {
public static void main(String[] args)
throws CompilationFailedException, IOException,
InstantiationException, IllegalAccessException {

final ClientOrder clientOrder = new ClientOrder();

clientOrder.run();

final Closure dsl =
(Closure)((Script) new GroovyClassLoader().parseClass(
new File("order.dsl")).newInstance()).run();

dsl.setDelegate(clientOrder);
final Object result = dsl.call();

List<Order> r = (List<Order>) result;
int val = 0;
for(Order x : r) {
val += (Integer)(x.getValue());
}
System.out.println(val);
}
}
You will have to make your DSL script return a Closure that then gets called within the Java application. Note that within Java we can now get complete handle over the Order classes which we defined in Groovy.

This is an example to demonstrate the usage of proper integration techniques while designing your DSL. In my upcoming book DSLs In Action I discuss many other techniques of integration for DSLs developed on the JVM. The above example is also an adaptation from what I discuss in the book in the context of integrating Groovy DSLs with Java application.

Thanks to Guillame Laforge and John Wilson for many suggestions on improving the Groovy DSL and it's interoperability with Java.
References
Published at DZone with permission of Debasish Ghosh, author and DZone MVB. (source)

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

Comments

Sindy Loreal replied on Sat, 2012/02/25 - 8:49am

The DSL example still shows how restricted writing a DSL in a language like Groovy is. The DSL isn't _truly_ natural - its the best possible within the confines of Groovy syntax.

The Fantom language takes a different approach, allowing any piece of text to be treated as a DSL, with no restrictions at all. More work for the author of the DSL, but a totally pure output.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.