Sunday, August 05, 2007

NFJS: Domain Specific Languages

Building DSLs in Static and Dynamic Languages was another "No Fluff, Just Stuff" session that I was really looking forward to. There has been somewhat of a buzz lately about Domain Specific Languages (DSLs), especially with the rise of languages like Groovy. With the little experience I have with Groovy I can see why it would be a great choice for creating a DSL. This session also explored creating DSLs with static languages, with particular emphasis on Java.

Neal Ford started the session by talking about how DSLs make programmers change their abstraction style. We should no longer think in trees or hierarchies, but in layers of abstraction using language – what Neal called "working at a higher level of abstraction." He also distinguished between declarative vs. imperative programming, which had echoes of what I heard in the Drools session.

To emphasize the importance of moving to a higher level of abstraction, Neal made the observation that "every non-trivial human behavior has a domain specific language." Here are some examples
  • "Iced Decaf Triple Grande Vanilla Skim with whip latte"
  • "Route 66, swinging, easy on the chorus, extra solo at the coda, and bump at the end"
  • "OMFG D00d Bob is t3h UBER 1337 R0XX0RZ LOL
The point of all this is, of course, to show that there is a disconnect between how we as programmers write code in our problem domain and how we speak in our problem domain. Can't we bring the two together? Wasn't object-oriented programming supposed to do that?

In typical programming, we code using an API that has an explicit context. For example
Coffee latte = new Coffee( Size.VENTI );
latte.setFatContent( FatContent.NON_FAT );
latte.setWhip( Whip.NONE );
latte.setFoam( Foam.NONE );
latte.setTemperature( Temp.EXTRA_HOT );
latte.setStrength( 5 );
The context, latte, is explicit and repeated. DSLs, however, have an implicit context which seems more natural to us. You would speak the code segment above thusly
Venti half-caf, non-fat, extra hot, no foam, no whip latte
See, you know the context so why repeat it. Why can't we code in this manner? Maybe we can.

Types of DSLs

There are two types of DSLs: internal and external. Internal DSLs sit on top of the base language and make the job of programming a little more fluent. An external DSL is a separate language that needs a "lexer and parser." One disappointment that I think many people had with this session was that we did not go into external DSLs enough.

What is meant by fluent with regards to internal DSLs is that lines of code are treated as sentences – complete units of thought. This makes code easier to read, but much harder to write. The simplest way to create an internal DSL with Java is to have methods return this so that actions can be chained, and context does not have to be repeated. Also, method names have to be a little more grammatical. Here's an example
Car car = new CarImpl().withMarketingDescriptionOf( new MarketingDescription( “Box” ) ).
andAttributeOf( “length”, “50.5” ).
andIncludesA( “ladder” ).
andAttributeOf( “lining type”, “cork” ) );
While I can see how this can be more fluent, I think the nature of Java still makes this look and feel clunky (a method named andAttributeOf, yuck!). Neal's comment about how this is harder to write is well taken. As an aside, Smalltalk's nature is better suited for this type of programming.

Neal then spent some time on best practices for method chaining and how to avoid some pitfalls like the stopping problem. I leave that for the reader to explore.

Internal DSLs in Groovy

Next, Neal talked about creating internal DSLs with Groovy. Looser syntax, closures, dynamic typing all make Groovy a better base language for writing DSLs.

One of the most powerful features in Groovy that makes a DSL easy to create is the category feature. This allows the developer to add additional behavior to classes not under their direct control. For instance, you could add a method to Integer called days. Why? So you could do something like this
2.days.fromToday.at( 4.pm )
This can be valid syntax in Groovy if implemented correctly. In Groovy 1.1 this will be even easier with the ExpandoMetaClass class (something borrowed from Grails). The analogy I give for this concept is that you are teaching an old dog new tricks.

Conclusion

This was a good session. It didn't live up to my expectations, but overall I enjoyed it. I do have some doubts, however, on the benefit of creating internal DSLs with Java. This idea seems like a better fit for Groovy or dynamic languages in general.

A note about the speaker. Neal Ford is a very competent speaker. His strength is his breadth of knowledge. I attended two of his sessions, which, by the way, covered two completely different topics, and in both cases you could easily tell he had a command of the subject matter. People who I talked to who attended his other sessions came to the same conclusion. He is not a pure speaker like Scott Davis, but he is very good.

Next up: A couple of disappointments

No comments: