GR8Conf 2009: Practical Groovy DSL by Guillaume Laforge
Upcoming SlideShare
Loading in...5
×
 

GR8Conf 2009: Practical Groovy DSL by Guillaume Laforge

on

  • 3,482 views

Guillaume Laforge, Groovy Project Manager, presents how to implement your own Domain-Specific Languages in Groovy

Guillaume Laforge, Groovy Project Manager, presents how to implement your own Domain-Specific Languages in Groovy

Statistics

Views

Total Views
3,482
Views on SlideShare
3,434
Embed Views
48

Actions

Likes
5
Downloads
98
Comments
0

4 Embeds 48

http://timszel.blogspot.com 22
http://www.slideshare.net 19
http://www.timszel.blogspot.com 5
http://timszel.blogspot.fr 2

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

GR8Conf 2009: Practical Groovy DSL by Guillaume Laforge GR8Conf 2009: Practical Groovy DSL by Guillaume Laforge Presentation Transcript

  • Practical Domain-Specific Languages with Groovy All the techniques to create your own DSLs Guillaume Laforge Head of Groovy Development
  • 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
  • nd a Ag e • The context and the usual issues we face • Some real-life examples of Domain-Specific Languages • Groovy’s DSL capabilities • Integrating a DSL in your application • Considerations to remember when designing your own DSL 5
  • The context
  • Subject Matter Experts, Business analysts...
  • 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
  • Lots of languages...
  • And in the end... ...nobody understands each other
  • Expressing requirements... 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 12
  • Towards more readability (1) 13
  • Towards more readability (1) 13
  • Towards more readability (1) 20% 13
  • Towards more readability (2) 14
  • Towards more readability (2) 14
  • Towards more readability (2) 80% 14
  • nd a Ag e • The context and the usual issues we face • Some real-life examples of Domain-Specific Languages • Groovy’s DSL capabilities • Integrating a DSL in your application • Considerations to remember when designing your own DSL 15
  • A collection of DSLs • In our everyday life, we’re surrounded by DSLs – Technical dialects – Notations – Business languages 16
  • Technical dialects 17
  • SQL
  • ^[w-.]+@([w-]){2,4}$ 19
  • Notations 20
  • 1. e4 e5 2. Nf3 Nc6 3. Bb5 a6
  • L2 U F-1 B L2 F B -1 U L2
  • Visual!
  • Business languages 24
  • 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 financial platform • Mathematica-like lingua for nuclear safety simulations • Market data feeds evolution scenarios • and more... 25
  • nd a Ag e • The context and the usual issues we face • Some real-life examples of Domain-Specific Languages • Groovy’s DSL capabilities • Integrating a DSL in your application • Considerations to remember when designing your own DSL 26
  • A flexible & 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 27
  • 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” 28
  • Optional typing • No need to bother with types or even generics – unless you want to! • Imagine an interest rate lookup table method returning some generified type: –Rate<LoanType, Duration, BigDecimal>[] lookupTable() { ... } def table = lookupTable() • No need to repeat the horrible generics type info! 29
  • 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 30
  • Optional parens & semis • Make statements and expressions look more like natural languages – move(left); – move left 31
  • 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) 32
  • BigDecimal by default • Main reason why financial 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) 33
  • 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) 34
  • Operator overloading a + b a.plus(b) a - b a.minus(b) • Currency amounts a * b a.multiply(b) – 15.euros + 10.dollars a / b a.divide(b) a % b a.modulo(b) • Distance handling a ** b a.power(b) – 10.kilometers - 10.meters a | b a.or(b) a & b a.and(b) • Workflow, concurrency a ^ b a.xor(b) – taskA | taskB & taskC a[b] a.getAt(b) a << b a.leftShift(b) • Credit an account a >> b a.rightShift(b) – account << 10.dollars +a a.positive() account += 10.dollars -a a.negative() account.credit 10.dollars ~a a.bitwiseNegate() 35
  • Groovy’s dynamic heart: The MOP! MetaObject Protocol
  • 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 37
  • 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 38
  • 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 39
  • 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 40
  • The Builder pattern
  • 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!” } } } } 42
  • A builder for HR • softskills { ideas { capture 2 formulate 3 } ... } knowhow { languages { java 4 groovy 5 } ... } 43
  • A builder for HR • softskills { ideas { capture 2 formulate 3 } ... } knowhow { languages { java 4 groovy 5 } ... } 43
  • 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... 44
  • 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 45
  • 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 46
  • 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 benefit from the new property 47
  • With an ExpandoMetaClass • Number.metaClass.getMeters = {-> new Distance(delegate, Unit.METERS) } 100.meters • Works for the class hierarchy for POJOs, and a flag exists to make it work for POGOs too • But the catch is it’s really a global change, so beware EMC enhancements collisions 48
  • Compile-time metaprogramming • Groovy 1.6 introduced AST Transformations • Compile-time == No runtime performance penalty! Transformation 49
  • AST Transformations • Two kinds of transformations – Global transformations •applicable to all compilation units – Local transformations •applicable to marked program elements •using specific marker annotations 50
  • 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 {} 51
  • Example #2: @Delegate Not just for managers! • You can delegate to fields 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 praise... 52
  • 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 file META-INF/services/ org.codehaus.groovy.transform.ASTTransformation • Add the fully qualified name of the class in that file 53
  • Local transformations • Same approach as Globale transformations • But you don’t need the META-INF file • Instead create an annotation to specify on which element the transformation should apply • @Retention(RetentionPolicy.SOURCE) @Target([ElementType.METHOD]) @GroovyASTTransformationClass( ["fqn.MyTransformation"]) public @interface WithLogging {...} 54
  • Example: the Spock framework • Changing the semantics of the original code • But keeping a valid Groovy syntax • @Speck class HelloSpock { def "can you figure out what I'm up to?"() { expect: name.size() == size where: name << ["Kirk", "Spock", "Scotty"] size << [4, 5, 6] } } 55
  • nd a Ag e • The context and the usual issues we face • Some real-life examples of Domain-Specific Languages • Groovy’s DSL capabilities • Integrating a DSL in your application • Considerations to remember when designing your own DSL 56
  • 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 57
  • 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”); 58
  • 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="events" script-source="classpath:dsl/ eventsChart.groovy" customizer-ref="eventsMetaClass" /> 59
  • Groovy’s own mechanisms • Eval – for evaluating simple expressions • GroovyShell – for more complex scripts and DSLs • GroovyClassLoader – the most powerful mechanism 60
  • Eval • Simple mechanism to evaluate math-like formulas • Eval.me ( ‘3*4’) Eval.x (1, ‘3*x + 4’) Eval.xy (1, 2, ‘x + y’) Eval.xyz(1, 2, 3, ‘x * y - z’) 61
  • GroovyShell • A Binding provides a context of execution – can implement lazy evaluation if needed • A base script class can be specified • def binding = new Binding() binding.mass = 22.3 binding.velocity = 10.6 def shell = new GroovyShell(binding) shell.evaluate(“mass * velocity ** 2 / 2”) 62
  • 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); 63
  • 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 file, etc. • Benefits – Business rules are not entangled in technical application code – Business rules can have their own lifecycle, without requiring application redeployments 64
  • nd a Ag e • The context and the usual issues we face • Some real-life examples of Domain-Specific Languages • Groovy’s DSL capabilities • Integrating a DSL in your application • Considerations to remember when designing your own DSL 65
  • Start small, with key concepts Beware overengineering!
  • Grow your language progressively
  • Get your hands dirty Play with the end-users
  • Let your DSL fly, it’s not yours, it’s theirs!
  • Tight feedback loop Iterative process
  • Stay humble. You can’t get it right the first time. Don’t design alone at your desk Involve the end users from the start
  • Playing it safe in a sandbox
  • Various levels of sandboxing • Groovy supports the usual Java Security Managers • Use metaprogramming tricks to prevent calling / instantiating certain classes • Create a special GroovyClassLoader AST code visitor to filter only the nodes of the AST you want to keep – ArithmeticShell in Groovy’s samples 73
  • Test, test, test! • Don’t just test for nominal cases – Explicitly test for errors! • Ensure end-users get meaningful error messages 74
  • nd a Ag e • Summary • Questions & Answers 75
  • Summary • Groovy’s a great fit for Domain-Specific Languages – Malleable & flexible syntax – Full object-orientation • Metaprogramming capabilities – Runtime metaprogramming – Compile-time metaprogramming • Groovy’s very often used for mission-critical DSLs 76
  • ? I kan haz my cheezburgr naw? Or do ya reely haz keshtionz?
  • Appendix 78
  • • http://www.flickr.com/photos/wheatfields/420088151/sizes/l/ • http://www.flickr.com/photos/therefromhere/518053737/sizes/l/ • http://www.flickr.com/photos/romainguy/230416692/sizes/l/ • http://www.flickr.com/photos/addictive_picasso/2874279971/sizes/l/ • http://www.flickr.com/photos/huangjiahui/3127634297/sizes/l/ • http://www.flickr.com/photos/25831000@N08/3064515804/sizes/o/ • http://www.flickr.com/photos/lanier67/3147696168/sizes/l/ • http://www.flickr.com/photos/ktb/4916063/sizes/o/ • http://www.flickr.com/photos/nathonline/918128338/sizes/l/ • http://www.flickr.com/photos/kevinsteele/39300193/sizes/l/ • http://commons.wikimedia.org/wiki/File:Brueghel-tower-of-babel.jpg • http://commons.wikimedia.org/wiki/File:Platypus.jpg • http://www.flickr.com/photos/joaomoura/2317171808/sizes/l/ • http://www.flickr.com/photos/wiccked/132687067/ • http://www.flickr.com/photos/xcbiker/386876546/sizes/l/ • http://www.flickr.com/photos/pietel/152403711/sizes/o/ 79
  • • http://www.flickr.com/photos/forezt/192554677/sizes/o/ • http://keremkosaner.files.wordpress.com/2008/04/ softwaredevelopment.gif • http://www.jouy.inra.fr • http://www.flickr.com/photos/ejpphoto/408101818/sizes/o/ • http://www.flickr.com/photos/solaro/2127576608/sizes/l/ • http://www.flickr.com/photos/biggreymare/2846899405/sizes/l/ • http://www.flickr.com/photos/timsamoff/252370986/sizes/l/ • http://www.flickr.com/photos/29738009@N08/2975466425/sizes/l/ • http://www.flickr.com/photos/howie_berlin/180121635/sizes/o/ • http://www.flickr.com/photos/yogi/1281980605/sizes/l/ • http://www.flickr.com/photos/dorseygraphics/1336468896/sizes/l/ 80