Date post: | 18-Dec-2014 |
Category: |
Technology |
Upload: | guillaume-laforge |
View: | 7,216 times |
Download: | 2 times |
09/15/2008
Internal Domain-Specific Languageswith Groovy
Guillaume Laforge
09/15/2008 2
Guillaume Laforge
• Groovy Project Manager• JSR-241 Spec Lead• VP Technology
• Initiator of the Grails framework• Co-author of Groovy in Action
• Speaker at numerous conferences–JavaOne, QCon, JavaDay, Sun TechDays, JavaPolis,
The Spring Experience, JAX, Dynamic Language World...
09/15/2008 3
Agenda
• Introduction to DSLs– Definition, characteristics, motivation
• What Groovy offers– The “MOP”, and Groovy tricks
• Integrating a DSL in your application– JSR-223, GroovyShell, GroovyClassLoader
• Considerations to keep in mind
• Summary• Questions & Answers
09/15/2008
Introduction to DSLs
09/15/2008 5
What’s a DSL?
• DSL: a Domain-Specific Language– Also called a “little language”
• Wikipedia: “A Domain-Specific Language is a programming language designed to be useful for a specific set of tasks”
• A DSL is a language that closely models a certain domain of knowledge or expertise, where the concepts are tied to the constructs of the language
09/15/2008 6
Characteristics of a DSL
• A DSL is a language– not necessarily Turing-complete
• Covers a particular domain of knowledge• Has a form: textual or graphical• Produces a result
– Configures objects, represents data structures, etc.
• Can be internal or external– Embedded in a host language (E-DSL)– Or standalone (usually a custom parser)
• Has certain quality attributes– Readability, writability, usability, testability, extensibility
09/15/2008 7
Examples of DSLs
• Technical– SELECT * FROM USER WHERE NAME LIKE ‘Guil%’– ^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$
• Notation– 1. e4 e5 2. Nf3 Nc6 3. Bb5 a6.– U R' U2 R U R' U R–
• Business– Risk calculation for insurance policies– Bank accounting rules– Human Resources: representation of employees skill set– Anti-malaria drug resistance simulation
09/15/2008 8
Towards more readable code? (1/2)
09/15/2008 8
Towards more readable code? (1/2)
09/15/2008 8
20%
Towards more readable code? (1/2)
09/15/2008 9
Towards more readable code? (1/2)
09/15/2008 9
Towards more readable code? (1/2)
09/15/2008 9
80%
Towards more readable code? (1/2)
09/15/2008 10
Why creating your own DSL?
• Use a more expressive language than a general programming language
• Share a common metaphore between developers and subject matter experts
• Have domain experts help design the business logic of an application
• Avoid cluttering business code with too much boilerplate technical code
• Cleanly seperate business logic from application code
• Let business rules have their own lifecycle
09/15/2008 11
Why Groovy?
• Why Groovy?– It allows you to create Internal / Embedded DSLs– Internal DSLs can be embedded easily
in your Java applications– Groovy lets you use more complex constructs
when you face the limits of your DSL
• Why not a custom parser?– A lexer/parser is complex to implement, maintain & use– It is harder to evolve
• Difficult to implement more complex constructs
09/15/2008
What Groovy offers
09/15/2008 13
Java vs Groovy
• Java– Class behaviour– The compiler knows all about behavior– Behavior hard-wired in the bytecode
• Groovy– Class and MetaClass behaviour
• Dynamic language
– The compiler doesn’t know much about behavior– Behaviour completely dynamic at runtime
09/15/2008 14
Groovy’s MOP to the rescue
• Meta Object Protocol
• Everything’s routed through the MOP– Method calls, property access, operators…– That’s why Groovy is a “dynamic language”
• You can completely customize the runtime behaviour of your Groovy code– Even 1+1=1? Yes! Really?
09/15/2008 15
Hooks into the runtime system
• GroovyObject– invokeMethod()– get/setProperty()
• Categories
• MetaClass– invokeConstructor() / method() / staticMethod()– invokeMissingMethod() / invokeMissingProperty()– get/setProperty()
• ExpandoMetaClass– Integer.metaClass.plus = { Integer i -> 1 }
09/15/2008 16
Operator overloading
• Simply implement certain methods:
– + a.plus(b)– - a.minus(b)– * a.multiply(b)– / a.divide(b)– % a.modulo(b)– | a.or(b)– & a.and(b)– a[b] a.getAt(b)– a << b a.leftShift(b)
– Currency arithmetics– 30.euros + 15.pounds
– Distances– 12.kilo.meters + 3.meters
– Parallelism, workflow– taskA & taskB | taskC
– Credit an account– account << 10.euros– account += 10.dollars
09/15/2008 17
Adding properties to numbers
• Through a category we can add methods & properties on numbers
class MyCategory { static Distance getMeters(Integer n) { new Distance(n, Distance.METER) } }
use (MyCategory) { println 3.meters}
09/15/2008 18
Same thing with EMC
• Grails contributed ExpandoMetaClass back to the Groovy code base
Integer.metaClass.getMeters = { -> new Distance(delegate, Distance.METER) }
println 3.meters
09/15/2008 19
The malleable syntax…
• Optional parentheses– move left– monster.move x: 3.meters, y: 4.meters– compare indicator: ‘NIKEI’, withFund: ‘XYZ’– account.debit amount: 30.euros, in: 3.days
• Native syntax for list and map literals– Lists: [1, 2, 3, 4]– Maps: [a: 1, b: 2, c: 3]– Ranges: Monday..Friday
09/15/2008 20
Builders for tree-structures
• Tree-structured data can be created with chained method calls taking closures as last argument
– new MarkupBuider().invoices { invoice(id: "4") { line "product 1" line "product 2" }}
• You can easily invent your own builder!
09/15/2008 21
BuilderSupport
• Implement BuilderSupport
• Methods to implement– createNode(name)– createNode(name, map)– createNode(name, value)– createNode(name, map, value)
• A value or a closure– nodeCompleted(parent, node)– postNodeCompletion(parent, node)
09/15/2008
A Human Resources DSL
• Skills of employees can be represented hierarchically, under various categories
• etre { idees { capture 2 formule 3 produit 4 }}faire { build { J2EE 4 }}
22
09/15/2008 23
Custom control structures
• You can create your own control structures by passing a closure as last parameter of a method:– unless(account.balance<0, {account.debit 10.dollars})
• Fortunately, there’s a shortcut notation:– unless ( account.balance < 0 ) {
account.debit 10.dollars}
• Creativity is your limit!– withLock (aLock) {…}– transactional { … }– async { … }– execute( within: 50.seconds ) { … }
09/15/2008
Integrating a DSLin your Application
09/15/2008 25
Integration mechanisms
• Java 6: JSR-223 / javax.script.*
• Groovy’s own mechanisms–GroovyShell–GroovyClassLoader
• Spring 2.0 dynamic language beans–Lang namespace–POGO customizer
09/15/2008 26
Java 6’s scripting APIs
• One API to rule them all!
• Scripting.dev.java.net provides a dedicated Groovy engine JAR– Drop it in your classpath!
• Example– ScriptEngineManager manager =
new ScriptEngineManager();ScriptEngine gEngine = manager.getEngineByName("groovy");String result = (String) gEngine.eval("’Foo’ * 2");
09/15/2008 27
Groovy’s own mechanisms
• Groovy offers various means of integrationfrom simple to more complex use cases
–GroovyShell: to evaluate more complex scripts and to let you use most of the Groovy DSL tricks
–GroovyClassLoader: a special class loader which gives you the full power of Groovy
09/15/2008 28
GroovyShell
• Evaluate expressions and scripts
• Pass values in and out through the binding
• Evaluated scripts can have a base class containing global functions or variables
• Method and property access can be intercepted with a base class for evaluated scripts or using a custom MetaClass
09/15/2008 29
GroovyShell example
def binding = new Binding()binding.mass = 22.3binding.velocity = 10.6def shell = new GroovyShell( binding )def expression = "mass * velocity ** 2 / 2" assert shell.evaluate(expression) == 1252.814
09/15/2008 30
GCL example
GroovyClassLoader gcl = new GroovyClassLoader();Class greetingClass = gcl.parseClass( new File("DSL.groovy"));
GroovyObject dsl = (GroovyObject)greetingClass.newInstance();
dsl.setMetaClass(myCustomDSLMetaClass);
09/15/2008 31
Spring 2.x integration
• Spring 2.x provides support for alternative language bean definitions and configuration– A POGO can be wired the like a POJO and be proxied– You can mix languages in your Spring application– POGO and POJO can be injected within each other– You can have Groovy beans “refreshed”!
• useful for DSLs with their own lifecycle
• Configuration of a POGO bean with the specific lang namespace, and a custom MetaClass
– <lang:groovy id="events" script-source="classpath:dsl/eventsChart.groovy" customizer-ref="eventsMetaClass" />
09/15/2008
Considerations to keep in mind
09/15/2008 33
Ensure DSL adoption
• Don’t force adoption!
• But...– Make users build their own DSL!
• Don’t create it alone at your desk• Involve end users regularly• Study how they use the DSL
– Guide users through what’s possible or not• Valid Groovy code
09/15/2008 34
Wash, rinse, repeat
• Iterative process
– Start simple
– Remember you can’t get it right the first time
– Go back brainstorming with subject matter experts, and improve it over time!
09/15/2008 35
Defensive programming
• A DSL should be run in a sandbox– Make sure users can’t crash the app!
• You can use Java’s security!–System.exit(1) anyone?
• Test, test, test!– Fail gracefully– Not just for valid cases: test for errors!– Give meaningful error messages
09/15/2008
Summary
09/15/2008 37
Summary
• DSLs are a great tool for sharing a common metaphor between experts and developers
• DSLs express the domain concepts without the usual boilerplate code
• Groovy’s maleable syntax and dynamic features makes it the perfect choice for creating DSLs and integrating them in a Java application
• Ensure adoption, work iteratively, and use defensive programming to ensure quality
09/15/2008 38
Resources
• Groovy: http://groovy.codehaus.org• Grails: http://grails.org
• Groovy blogs: http://groovyblogs.org• AboutGroovy: http://aboutgroovy.com
• G2One: http://www.g2one.com
09/15/2008
Question & Answers