Six Myths about Ontologies: The Basics of Formal Ontology
Â
Practical Groovy DSL
1. Guillaume Laforge / Groovy Project Manager / SpringSource
Practical Domain-Specific
Languages with Groovy
All the techniques to create your own DSL.
1
2. Guillaume Laforge
⢠Groovy Project Manager
⢠JSR-241 Spec Lead
⢠Head of Groovy Development
at SpringSource
⢠Initiator of the Grails framework
⢠Co-author of Groovy in Action
⢠Speaker: JavaOne, QCon, JavaZone, Sun TechDays,
Devoxx, The Spring Experience, JAX, Dynamic
Language World, IJTC, and more...
3. A few words about Groovy
⢠Groovy is a dynamic language for the JVM
⢠with a Meta Object Protocol
⢠compiles directly to bytecode, seamless Java interop
⢠Open Source ASL 2 project hosted at Codehaus
⢠Relaxed grammar derived from Java 5
⢠+ borrowed good ideas from Ruby, Python, Smalltalk
⢠Fast... for a dynlang on the JVM
⢠Closures, properties, optional typing, BigDecimal
by default, nice wrapper APIs, and more...
4. a
d
n
e
g
A ⢠The context and
the usual issues we face
⢠Some real-life examples of
Domain-SpeciďŹc Languages
⢠Groovyâs DSL capabilities
⢠Integrating a DSL
in your application
⢠Considerations to remember
when designing your own DSL
7. Developer producing
LOLCODE
HAI
CAN HAS STDIO?
I HAS A VAR
IM IN YR LOOP
UP VAR!!1
VISIBLE VAR
IZ VAR BIGGER THAN 10?
KTHXBYE
IM OUTTA YR LOOP
KTHXBYE
11. DSL: a potential solution?
⢠Use a more expressive language than a general purpose one
⢠Share a common metaphore of understanding between developers
and subject matter experts
⢠Have domain experts help with the design of the business logic of
an application
⢠Avoid cluttering business code with too much boilerplate
technical code
⢠Cleanly separate business logic from application code
⢠Let business rules have their own lifecycle
18. a
d
n
e
g
A ⢠The context and
the usual issues we face
⢠Some real-life examples of
Domain-SpeciďŹc Languages
⢠Groovyâs DSL capabilities
⢠Integrating a DSL
in your application
⢠Considerations to remember
when designing your own DSL
19. A collection of DSLs
⢠In our everyday life, weâre surrounded by DSLs
⢠Technical dialects
⢠Notations
⢠Business languages
28. Real-life Groovy examples
⢠Anti-malaria drug resistance simulation
⢠Human Resources employee skills representation
⢠Insurance policies risk calculation engine
⢠Loan acceptance rules engine for a ďŹnancial platform
⢠Mathematica-like lingua for nuclear safety simulations
⢠Market data feeds evolution scenarios
⢠and more...
29. a
d
n
e
g
A ⢠The context and
the usual issues we face
⢠Some real-life examples of
Domain-SpeciďŹc Languages
⢠Groovyâs DSL capabilities
⢠Integrating a DSL
in your application
⢠Considerations to remember
when designing your own DSL
30. A ďŹexible & malleable syntax
⢠No need to write full-blown classes, use scripts
⢠Optional typing (def)
⢠in scripts, you can even omit the def keyword
⢠Native syntax constructs
⢠Parentheses & semi-colons are optional
⢠Named arguments
⢠BigDecimal by default for decimal numbers
⢠Closures for custom control structures
⢠Operator overloading
31. Scripts vs classes
⢠Hide all the boilerplate technical code
⢠an end-user doesnât need to know about classes
⢠public class Rule {
public static void main(String[] args) {
System.out.println(âHelloâ);
}
}
⢠println âHelloâ
32. Optional typing
⢠No need to bother with types or even generics
⢠unless you want to!
⢠Imagine an interest rate lookup table method
returning some generiďŹed type:
⢠Rate<LoanType, Duration, BigDecimal>[]
lookupTable() { ... }
def table = lookupTable()
⢠No need to repeat the horrible generics type info!
33. Native syntax constructs
⢠Lists
⢠[Monday, Tuesday, Wednesday]
⢠Maps
⢠[CA: âCaliforniaâ, TX: âTexasâ]
⢠Ranges
⢠def bizDays = Monday..Friday
⢠def allowedAge = 18..65
⢠You can create your own custom ranges
34. Optional parens & semis
⢠Make statements and expressions
look more like natural languages
⢠move(left);
â˘move left
35. Named arguments
⢠In Groovy you can mix named and unnamed
arguments for method parameters
⢠named params are actually put in a map parameter
⢠plus optional parens & semis
⢠take 1.pill,
of: Chloroquinine,
after: 6.hours
⢠Corresponds to a method signature like:
⢠def take(Map m, MedicineQuantity mq)
36. BigDecimal by default
⢠Main reason why ďŹnancial institutions often decide to
use Groovy for their business rules!
⢠Although these days rounding issues are overrated!
⢠Java vs Groovy for a simple interpolation equation
⢠BigDecimal uMinusv = c.subtract(a);
BigDecimal vMinusl = b.subtract(c);
BigDecimal uMinusl = a.subtract(b);
return e.multiply(uMinusv)
.add(d.multiply(vMinusl))
.divide(uMinusl, 10, BigDecimal.ROUND_HALF_UP);
⢠(d * (b - c) + e * (c - a)) / (a - b)
37. Custom control structures,
thanks to closures
⢠When closures are last, they can be put âoutâ of the
parentheses surrounding parameters
⢠unless (account.balance > 100.euros,
{ account.debit 100.euros })
⢠unless (account.balance > 100.euros) {
account.debit 100.euros
}
⢠Signature def unless(boolean b, Closure c)
38. Operator overloading
⢠Currency amounts
a+b a.plus(b)
â˘
a-b a.minus(b)
15.euros + 10.dollars
a*b a.multiply(b)
⢠Distance handling
a/b a.divide(b)
a%b a.modulo(b)
⢠10.kilometers - 10.meters
a ** b a.power(b)
a|b a.or(b)
⢠WorkďŹow, concurrency
a&b a.and(b)
â˘
a^b a.xor(b) taskA | taskB & taskC
a[b] a.getAt(b)
⢠Credit an account
a << b a.leftShift(b)
a >> b a.rightShift(b)
⢠account << 10.dollars
+a a.positive()
account += 10.dollars
-a a.negative()
account.credit 10.dollars
~a a.bitwiseNegate()
40. Groovyâs MOP
⢠All the accesses to methods, properties, constructors,
operators, etc. can be intercepted thanks to the MOP
⢠While Javaâs behavior is hard-wired at compile-
time in the class
⢠Groovyâs runtime behavior is adaptable at
runtime through the metaclass.
⢠Different hooks for changing the runtime behavior
⢠GroovyObject, custom MetaClass implementation,
categories, ExpandoMetaClass
41. GroovyObject
⢠All instances of classes created in Groovy implement
the GroovyObject interface:
⢠getProperty(String name)
⢠setProperty(String name, Object value)
⢠invokeMethod(String name, Object[]
params)
⢠getMetaClass()
⢠setMetaClass(MetaClass mc)
⢠A GO can have âpretendedâ methods and properties
42. MetaClass
⢠The core of Groovyâs MOP system
⢠invokeConstructor()
⢠invokeMethod() and invokeStaticMethod()
⢠invokeMissingMethod()
⢠getProperty() and setProperty()
⢠getAttribute() and setAttribute()
⢠respondsTo() and hasProperty()
⢠MetaClasses can change the behavior of existing third-
party classes â even from the JDK
43. ExpandoMetaClass
⢠A DSL for MetaClasses!
⢠MoneyAmount.metaClass.constructor = { ... }
Number.metaClass.getDollars = { ... }
Distance.metaClass.toMeters = { ... }
Distance.metaClass.static.create = { ... }
⢠To avoid repetition of Type.metaClass, you can pass a
closure to metaClass { ... }
⢠The delegate variable in closure represents the
current instance, and it the default parameter
45. The Groovy MarkupBuilder
⢠def mkp = new MarkupBuilder()
mkp.html {
head {
title âGroovy in Actionâ
}
body {
div(width: â100â) {
p(class: âpara) {
span âBest book ever!â
}
}
}
}
46. A builder for HR
⢠softskills {
ideas {
capture 2
formulate 3
}
...
}
knowhow {
languages {
java 4
groovy 5
}
...
}
47. A builder for HR
⢠softskills {
ideas {
capture 2
formulate 3
}
...
}
knowhow {
languages {
java 4
groovy 5
}
...
}
48. Builders
⢠Builders are...
⢠a mechanism for creating any tree-structered graph
⢠the realization of the GoF builder pattern at the
syntax level in Groovy
⢠simply a clever use of chained method invocation,
closures, parentheses omission, and use of the
GroovyObject methods
⢠Existing builders
⢠XML, Object graph, Swing, Ant, JMX, and more...
49. The clever trick
⢠GroovyObject#invokeMethod() is used to catch all
non-existing method calls in the context of the
builder
⢠The nesting of closures visually shows the level of
nesting / depth in the tree
⢠builder.m1(attr1:1, attr2:2, { builder.m2(..., {...}) }
becomes equivalent to
builder.m1(attr1:1, attr2:2) { m2(...) {...} }
thanks to parens omission
50. Adding properties to numbers
⢠Three possible approaches
⢠create a Category
⢠a category is a kind of decorator for default MCs
⢠create a custom MetaClass
⢠a full-blown MC class to implement and to set on
the POGO instance
⢠use ExpandoMetaClass
⢠friendlier DSL approach but with a catch
51. With a Category
⢠class DistanceCategory {
static Distance getMeters(Integer self) {
new Distance(self, Unit.METERS)
}
}
use(DistanceCategory) {
100.meters
}
⢠Interesting scope: thread-bound & lexical
⢠But doesnât work across the hierarchy of classes
⢠ie. subclasses wonât beneďŹt from the new property
52. With an ExpandoMetaClass
⢠Number.metaClass.getMeters = {->
new Distance(delegate, Unit.METERS)
}
100.meters
⢠Works for the class hierarchy for POJOs, and a ďŹag
exists to make it work for POGOs too
⢠But the catch is itâs really a global change, so beware
EMC enhancements collisions
54. AST Transformations
⢠Two kinds of transformations
⢠Global transformations
⢠applicable to all compilation units
⢠Local transformations
⢠applicable to marked program elements
⢠using speciďŹc marker annotations
55. Example #1: @Singleton
⢠Letâs revisit this evil{(anti-)pattern
public class Evil
ďŹ
public static final Evil instance = new Evil ();
private Evil () {}
Evil getInstance() { return instance; }
}
⢠In Groovy
@Singleton() class Evil {}
ďŹ
⢠Also a âlazyâ version
@Singleton(lazy = true) class Evil {}
ďŹ
56. Example #2: @Delegate
Not just for Managers
⢠You can delegate to ďŹelds of your classes
⢠class Employee {
def doTheWork() { âdoneâ }
}
class Manager {
@Delegate
Employee slave = new Employee()
}
def god = new Manager()
assert god.doTheWork() == âdoneâ
⢠Damn manager who will get all the praiser...
57. Global transformations
⢠Implement ASTTransformation
⢠Annotate the transfo specifying a compilation phase
⢠@GroovyASTTransformation(phase=CompilePhase.CONVERSION)
public class MyTransformation
implements ASTTransformation {
public void visit(ASTNode[] nodes, SourceUnit unit)
{ ... }
}
⢠For discovery, create the ďŹle META-INF/services/
org.codehaus.groovy.transform.ASTTransformation
⢠Add the fully qualiďŹed name of the class in that ďŹle
58. Local transformations
⢠Same approach as Globale transformations
⢠But you donât need the META-INF ďŹle
⢠Instead create an annotation to specify on which
element the transformation should apply
⢠@Retention(RetentionPolicy.SOURCE)
@Target([ElementType.METHOD])
@GroovyASTTransformationClass(
[quot;fqn.MyTransformationquot;])
public @interface WithLogging {...}
59. Example: the Spock framework
⢠Changing the semantics of the original code
⢠But keeping a valid Groovy syntax
⢠@Speck
class HelloSpock {
def quot;can you figure out what I'm up to?quot;() {
expect:
name.size() == size
where:
name << [quot;Kirkquot;, quot;Spockquot;, quot;Scottyquot;]
size << [4, 5, 6]
}
}
60. a
d
n
e
g
A ⢠The context and
the usual issues we face
⢠Some real-life examples of
Domain-SpeciďŹc Languages
⢠Groovyâs DSL capabilities
⢠Integrating a DSL
in your application
⢠Considerations to remember
when designing your own DSL
61. Various integration mechanisms
⢠Java 6âs javax.script.* APIs (aka JSR-223)
⢠Springâs language namespace
⢠Groovyâs own mechanisms
⢠But a key idea is to externalize those DSL programs
⢠DSL programs can have their own lifecycle
⢠no need to redeploy an application because of a
rule change
⢠business people wonât see the technical code
62. Java 6âs javax.script.* API
⢠Groovy 1.6 provides its own implementation of the
javax.script.* API
⢠ScriptEngineManager mgr =
new ScriptEngineManager();
ScriptEngine engine =
mgr.getEngineByName(âGroovyâ);
String result = (String)engine.eval(â2+3â);
63. Springâs lang namespace
⢠POGOs (Plain Old Groovy Objects) can be pre-
compiled as any POJO and used interchangeably with
POJOs in a Spring application
⢠But Groovy scripts & classes can be loaded at runtime
through the <lang:groovy/> namespace and tag
⢠Reloadable on change
⢠Customizable through a custom MetaClass
⢠<lang:groovy id=quot;eventsquot;
script-source=quot;classpath:dsl/eventsChart.groovyquot;
customizer-ref=quot;eventsMetaClassquot; />
64. Groovyâs own mechanisms
⢠Eval
⢠for evaluating simple expressions
⢠GroovyShell
⢠for more complex scripts and DSLs
⢠GroovyClassLoader
⢠the most powerful mechanism
66. GroovyShell
⢠A Binding provides a context of execution
⢠can implement lazy evaluation if needed
⢠A base script class can be speciďŹed
⢠def binding = new Binding()
binding.mass = 22.3
binding.velocity = 10.6
def shell = new GroovyShell(binding)
shell.evaluate(âmass * velocity ** 2 / 2â)
67. GroovyClassLoader
⢠Most powerful mechanism
⢠could also visit or change the AST
⢠scripts & classes can be loaded from elsewhere
⢠more control on compilation
⢠GroovyClassLoader gcl =
new GroovyClassLoader();
Class clazz = gcl.parseClass(
new File(âf.groovyâ));
GroovyObject instance =
(GroovyObject)clazz.newInstance();
instance.setMetaClass(customMC);
68. Externalize business rules
⢠Although Groovy DSLs can be embedded in normal
Groovy classes, you should externalize them
⢠Store them elsewhere
⢠in a database, an XML ďŹle, etc.
⢠BeneďŹts
⢠Business rules are not entangled
in technical application code
⢠Business rules can have their own lifecycle,
without requiring application redeployments
69. a
d
n
e
g
A ⢠The context and
the usual issues we face
⢠Some real-life examples of
Domain-SpeciďŹc Languages
⢠Groovyâs DSL capabilities
⢠Integrating a DSL
in your application
⢠Considerations to remember
when designing your own DSL
77. Various levels of sandboxing
⢠Groovy supports the usual Java Security Managers
⢠Use metaprogramming tricks to prevent calling /
instanciating certain classes
⢠Create a special GroovyClassLoader AST code visitor
to ďŹlter only the nodes of the AST you want to keep
⢠ArithmeticShell in Groovyâs samples
78. Test, test, test!
⢠Donât just test for nominal cases
⢠Explicitely test for errors!
⢠Ensure end-users get meaninful error messages
79. a
d
n
e
g
A
⢠Summary
⢠Questions & Answers
80. Summary
⢠Groovyâs a great ďŹt for Domain-SpeciďŹc Languages
⢠Malleable & ďŹexible syntax
⢠Full object-orientation
⢠Metaprogramming capabilities
⢠Runtime metaprogramming
⢠Compile-time metaprogramming
⢠Groovyâs very often used for mission-critical DSLs
81. ?
I kan haz my cheezburgr naw?
Or do ya reely haz keshtionz?