4. DSL: Domain Specific Language
Small, Specific “Mini-language” geared toward a Domain
Favor conciseness and readability in a narrow domain over Turing
Completeness
External
• Involve Lexer/Parser
• Ant, SQL
Internal (Embedded DSLs/Fluent Interfaces)
• Defined inside a more general purpose language
• Gant/Rake
DSL vs. Framework vs. API?
• What is OpenGL?
5. Why use Groovy to Write DSLs?
Groovy is a natural extension for Java Developers
• IDE support for “free”
At a minimum Groovy is “less verbose Java”
At a maximum Groovy lets you change the language itself
• dynamic methods and properties and operator overloading
Groovy has built-in DSLs (Builders)
8. class Game {
void go(dir) {
println "You go $dir"
}
void look() {
println "You look around and see nothing"
}
void take(it) {
println "You take the $it"
}
...
}
13. Metaprogramming: Dynamic Properties
def propertyMissing(String name) {
if (metaClass.respondsTo(this, name)) {
this."$name"()
}
name
}
go north
look
take dagger
15. Closures and “with”
new GroovyShell().evaluate(“game.with{${playerInput}}”)
3.times {
go north
}
look
take dagger
16. One more thing... (the Synonym Problem)
look
> You see a dagger on the ground
get dagger
> I don’t understand that
grab dagger
> I don’t understand that
Just let me have the %$!#@ dagger!
> I don’t understand that
17. Metaprogramming: Dynamic Methods
def methodMissing(String name, args) {
if (["grab", “hold", "yoink"].contains(name)) {
this.take(args[0])
}
}
look
> You see a dagger on the ground
grab dagger
> You take the dagger
You’re darn right I do!
> I don’t understand that
21. The Challenge
Replace Mainframe “Budget Book Generator” application
Did not interact with the live budget data
Over 3 hours to produce a book
Very manual process (print the book, rescan as a PDF of images)
Somewhat dated look
22. The Solution
Java SE Swing Application deployed locally via Webstart
Time to produce final PDF reduced from three hours to a few minutes
Operated Directly with the Budget Data
PDF as native output: searchable, indexable
29. How do we go from Budget Objects to Book Objects?
•Departments
•Organizations
•Appropriations
•Positions
•BookSections
•PageElements
Can we write a DSL to create a book?
30. Building Drafts with ContentBuilder
interface ContentBuilder
- Draft build(ContentData)
32. BookSection bookSection = new BookSection ("Positions");
TableHeaderElement header = new TableHeaderElement();
header.addHeader(new TableHeader(0.05));
header.addHeader(new TableHeader(0.3, "Position",
Formatter.LeftAligned));
bookSection.addPageElement(header);
for (TableSection section : tableSections) {
TableSectionElement sectionElement = new TableSectionElement();
sectionElement.addColumn(new TableColumn(2, section.getName());
bookSection.addPageElement(sectionElement);
...
33. BookSection bookSection = new BookSection ("Positions");
TableHeaderElement header = new TableHeaderElement();
Remember to fill out your Objects in Triplicate
1 2 3
34. BookSection bookSection = new BookSection ("Positions");
TableHeaderElement header = new TableHeaderElement();
header.addHeader(new TableHeader(0.05));
header.addHeader(new TableHeader(0.3, "Position",
Formatter.LeftAligned));
bookSection.addPageElement(header);
Declared here
Added here
35. BookSection bookSection = new BookSection ("Positions");
TableHeaderElement header = new TableHeaderElement();
header.addHeader(new TableHeader(0.05));
header.addHeader(
new TableHeader(0.3, "Position", Formatter.LeftAligned));
•What do these arguments mean?
•What is “0.05”?
•What is “Position”?
39. Let’s Take a Step Back…
Both Java versions “work”, but are verbose and not overly readable
Need better support for building complex object graphs at the
language level
• The structure of the code should mirror the structure of the objects
(declarative syntax)
• Ceremony vs. Essence
We need a “Builder”
40. Well, Groovy has Builders
MarkupBuilder
• Tag-based markup (XML, HTML)
SwingBuilder
• GUIs
GraphicsBuilder
• Java2D
NodeBuilder
• General Object graph (Nodes)
MetaBuilder
• Builder of Builders
46. Instead of this:
frame ([title:"Swing Builder Test"], {
flowLayout()
button(text:"Press Me")
})
frame (title:"Swing Builder Test") {
flowLayout()
button(text:"Press Me")
}
You have this:
47. Better Still: FactoryBuilderSupport
Used by SwingBuilder
Register “Factories” that know how to create your objects
Convenience AbstractFactory requires only one method:
• Object newInstance ( FactoryBuilderSupport builder,
Object name,
Object value,
Map attributes )
48. A Look Inside SwingBuilder
public SwingBuilder() {
...
registerFactory("button", new RichActionWidgetFactory(JButton));
...
}
57. for (Position position : positions) {
TableRowElement row = new TableRowElement();
row.setStyleName(“table.row”);
row.addColumn(new TableColumn(position.getAccountId()));
row.addColumn(new TableColumn(position.getTitle()));
bookSection.newPageElement(row);
}
positions.each { position ->
tableRowElement (styleName:"table.row") {
tableColumn position.accountId
tableColumn position.title
}
}
Before:
After:
58. Free Things “Built-In” to the Builder
Wiring of map attributes to object properties
Preservation of Object hierarchy
Automatic management of closure delegates so that the “Builder” is
always in scope
59. Closures: What’s a Delegate?
def htmlBuilder = new MarkupBuilder()
htmlBuilder.html {
body {
h1 "Hi There!"
}
}
“html”, “body”, and “h1” don’t exist in scope, but rather are handled
by the Builder
htmlBuilder needs to be set as the closure’s Delegate
60. …But the Methods Don’t Exist in the Builder Either
def xmlBuilder = new MarkupBuilder()
xmoBuilder.invoices {
invoice {
lineitem ...
}
}
The Builder “pretends” to have these methods
invokeMethod is overridden to write out the markup or (in the case of
SwingBuilder/BookSectionBuilder) dispatch the method to the correct
Factory
(Metaprogramming: Dynamic Methods)
62. First Write a Factory
private static class ChartFactory extends AbstractFactory {
public Object newInstance(...) {
return new Chart(value.toString());
}
public void setParent(...) {
TableCell cell = (TableCell) parent;
cell.addContent(child.toString());
}
}
63. Then Register the Factory…
public BookSectionBuilder() {
registerFactory("bookSection", new BookSectionFactory());
registerFactory("textElement",
new PageElementFactory(TextElement.class));
...
registerFactory("chart", new ChartFactory());
}
64. …And It’s Ready to use in the Builder
tableRowElement {
tableColumn (columnSpan:2) {
chart dept.fundPercent
}
tableColumn {
chart allFundTotal
}
}
65. Is a DSL Right for Me?
Are the people who maintain your project different than those who
initially developed it?
DSLs give the “gift of documentation”
Is your project part of a domain?
Using Dynamic Languages like Groovy make DSLs more Painless
66. Resources
Writing an Adventure game DSL in LISP: http://www.lisperati.com/casting.html
http://groovy.codehaus.org/FactoryBuilderSupport
http://docs.codehaus.org/display/GROOVY/MetaBuilder
http://docs.codehaus.org/display/GROOVY/Writing+Domain-Specific+Languages
http://www.agiledeveloper.com/blog/PermaLink.aspx?guid=ce021e6d-b0c2-4d83-8b69-
c163c290983d
http://blogs.citytechinc.com/sanderson/
King’s Quest and Zork images taken from Wikipedia.
67. “Groovy is like Gravy, except that it has the „Groove‟ in it.”
-Meredith Anderson (5 Year Old)