Date post: | 07-Nov-2014 |
Category: |
Technology |
Upload: | paulbowler |
View: | 850 times |
Download: | 1 times |
DSL’s With GroovyPaul Bowler
Senior Consultant, OpenCredo
“Domain-Specific”Languages?
Domain?
–nouna field of action, thought,
influence, etc.
Domains, domains, domains...!
Domain Language?
• Narrow, specialised focus
• Contains syntax and grammar to interact with the domain
• Few, if any, control structures (not ‘Turing Complete’)
• Communicates instructions and configuration
So, like these?
...or these?
yes, ... and no!
What then?
“...most DSL’s are merely a thin facade over a library or framework.”
Martin Fowler, ThoughtWorks
OS
PlatformFramework
DSL
What’s wrong with code?
DeveloperDomain Expert
Cognitive Gap
How do DSL’s help?
• Expressive - Well-designed abstractions
• Simple - Reduce cognitive overhead
• Focussed - Deliver clarity of intent
• Inclusive - Empower the domain expert
What’s wrong with code?
DeveloperDomain Expert
Cognitive Gap
Close the Cognitive Gap
DeveloperDomain Expert
How?
• External DSL - Separate from the ‘main’ languagee.g. SQL, XML, JSON
• Internal DSL - Valid code in the ‘main’ language
• Language Workbench - Specialised ‘IDE’ as editing environment with ‘human-centric’ graphical representation
Why Groovy?
Less noise More sugar
How?
• Builders
• Categories
• Meta-Object Protocol (MOP)
• Abstract Syntax Tree (AST) Transformations
Builders
• Great for hierarchical structures, such as trees, collections of collections, etc.
• Built in support for XML, HTML, Swing, etc
• Roll your own by extending ‘BuilderSupport’ class
Using buildersdef writer = new StringWriter()def xml = new MarkupBuilder(writer)
xml.team(nickname:'DreamTeam') { member(name:'John Smith', age:'32') { nationality('British') job(type:'full-time', 'Java Developer') } member(name:'Sue Perb', age:'25') { nationality('Australian') job(type:'full-time', 'Tester') } member(name:'Fred Bloggs', age:'30') { nationality('Irish') job(type:'contract', 'Build Guru') }}
Builder Methodsclass MyBuilder extends BuilderSupport {
public void setParent(Object parent, Object child) { }
public createNode(Object name) { } public createNode(Object name, Object value) { } public createNode(Object name, Map attributes) { } public createNode(Object name, Map attributes, Object value) { }}
Categories• Allow syntax extensions, e.g:
def nextWeek = today + 7.days
• Any public static method is a candidate to be a category method
• The type of the first parameter is the target type that may have the new methods ‘injected’
• No need to extend a particular class or implement an interface
• Gotcha! Methods added to classes for limited time using ThreadLocal. Also requires wrapping with use() method.
Category Exampleimport org.apache.commons.lang.StringUtils
class StringCategory { static capitalize( String self ){ StringUtils.capitalize(self) }
static normalize( String self ){ self.split("_").collect { word -> word.toLowerCase().capitalize() }.join("") }}
use( StringCategory ) { assert "Groovy" == "groovy".capitalize() assert "CamelCase" == "CAMEL_CASE".normalize()}
MOP
• Every object inherits from groovy.lang.GroovyObject which contains MetaClass
• ExpandoMetaClass extensions and hooks:invokeMethod(),getProperty(), methodMissing()and propertyMissing()
• Used extensively in Grails - Dynamic Finders (e.g. book.findAllByAuthor)
• Less code and cleaner implementation than categories
• Uses closures and delegates
• Can add or update/overload existing methods
• No need for Use()
• Can overload static methods
• Has longer lifespan than categories
• Inheritance not enabled by default for performance reasons
MOP vs Categories
MOP Example
import org.apache.commons.lang.StringUtils
String.metaClass.capitalize = { StringUtils.capitalize(delegate)}
String.metaClass.normalize = { delegate.split("_").collect { word -> word.toLowerCase().capitalize() }.join("")}
assert "Groovy" == "groovy".capitalize()assert "CamelCase" == "CAMEL_CASE".normalize()
Abstract Syntax Tree
• Compile-time Meta Programming allowing developers to hook into the compilation process
• No runtime performance penalty
• Global transformations using service locator file
• Local transformations using annotations
• Built using... builders!
• Transformations must be performed in one of the nine defined compiler phases
Compilation Phases1. Initialization: source files are opened and environment
configured2. Parsing: the grammar is used to to produce tree of tokens
representing the source code3. Conversion: An abstract syntax tree (AST) is created from
token trees.4. Semantic Analysis: Performs consistency and validity checks that
the grammar can't check for, and resolves classes.5. Canonicalization: Complete building the AST6. Instruction Selection: instruction set is chosen, for example java5
or pre java57. Class Generation: creates the binary output in memory8. Output: write the binary output to the file system9. Finalisation: Perform any last cleanup
Popular Transformations
• Bindable and Vetoable transformation
• Category and Mixin transformations
• Delegate transformation
• Immutable transformation
• Lazy transformation
• Newify transformation
• PackageScope transformation
• Singleton transformation
It all looks so complicated!
Case Study - Grint‘Groovier Integration!’
Grint
• Groovy DSL for Integration Patterns
• Deployable as a Grails plugin
• Baked for our DSL course
• Already in production...
A Groovier Approach to Integration
• Events cause messages
• EDA
• See Event-driven IO to get an idea
• Node.js
• Akka
• Even Spring Integration...
Thank you