Implementing DSLs in Groovy Matt Secoske http://blog.secosoft.net http://objectpartners.com
DSL? Groovy?
D omain  S pecific  L anguage: A language that clearly  expresses the ideas of a  particular domain
aka: fluent / humane interfaces problem oriented languages little / mini languages macros (as in Excel)
Domain Specific  Language
this:
not this:
this:
not this:
A DSL is just another tool in our toolbox.  It’s a jig to help us build our applications. picture copyrighted @2007 Pat Warner, http://www.patwarner.com
why?
bring the syntax closer to the  domain   (the experts language)
mrMustard .didIt( Murder .of( mrGreen )) .in( theKitchen ) .with( theCandleStick )
/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/
class CreateQuizComments < ActiveRecord::Migration   def self.up   create_table :quiz_comments do |t|   t.column :name, :string   t.column :email, :string   t.column :url, :string   t.column :comment, :text    end   end   def self.down   drop_table :quiz_comments   end end
Goal:   make the  problem  easier to    understand
(My)  Ultimate  Goal:  Be able to give the code to  the experts, and have them  use  it
Types of DSLs: - Internal - External - Functional - Object Oriented
Internal DSLs  - extending a language - can be whatever  you  want - within the laws of the base    language
Groovy?
A Compelling Story: - Much of the flexibility of Ruby - Familiar Java idioms and syntax - Is focused on the JVM  - and Java “pain points”
Groovy has good DSL support:  - introspection / reflection (ability to parse(itself)) Meta-Object Protocol  - syntactic sugar Builders  Duck Typing Categories ExpandoMetaClass Named Parameters Operator Overloading
Introspection / Reflection - Access to the AST at Class loading time - Can run/load code at any time
class  MyGroovy  extends  GroovyClassLoader { protected  CompilationUnit createCompilationUnit(CompilerConfiguration config, CodeSource source) { CompilationUnit cu =  super .createCompilationUnit(config, source); cu.addPhaseOperation( new  PrimaryClassNodeOperation() { public   void   call (SourceUnit source,  GeneratorContext context,  ClassNode classNode)  throws  CompilationFailedException { String name = classNode.getName(); if  (name.endsWith( &quot;Foo&quot; )) { BlockStatement code =  new  BlockStatement(); classNode.addMethod( ”bar&quot; ,Opcodes.ACC_PUBLIC, null , null , null ,code); } } },Phases.CONVERSION); return  cu; } } Adapted from blackdrag’s blog:  http://blackdragsview.blogspot.com/2006/11/chitchat-with-groovy-compiler.html
Meta Object Protocol   - Determines what’s running at Runtime  - add/modify/intercept methods, properties MetaClass Class getFoo() doesntExistOnClass() Other Code
more Groovy syntactic sugar
Duck typing : flexible code if it walks like a duck… and it quacks like a duck… its probably a …. a =  ”a string&quot; println  a. class  // => class java.lang.String a =  1 println  a. class  // => class java.lang.Integer
builders : structured data   swing =  new  SwingBuilder() frame = swing.frame( title: 'Life'  ) {    vbox {    panel( id: 'canvas' ) {   vstrut(height: 300 )   hstrut(width: 300 )   }   hbox {   glue()    button( text: 'Next' , actionPerformed:{evolve()})    } }} frame.pack() frame.show()
Using “ use ”  (Groovy Categories) class  MeasureCategory {   static  Measurement getLbs( final  Integer self) {   new  Measurement(self, Measurement.Pounds)   } } aClosure = {->   println   4 .lbs.of( &quot;Sugar&quot; )   } use (MeasureCategory. class ) {   println   1 .lbs.of( &quot;Carrots&quot; )  // -> 1 Pounds of Carrots   aClosure. call ()   // -> 4 Pounds of Sugar }
Using  ExpandoMetaClass: metaClass =  new  ExpandoMetaClass(Integer. class , true ) metaClass.getLbs << {->   &quot;${delegate} pounds&quot; } metaClass.initialize() i =  1 ; println  i // => 1   println  i. class // => java.lang.Integer println  i.lbs // => 1 pounds
Using  ExpandoMetaClass: metaClass =  new  ExpandoMetaClass(Integer. class , true ) metaClass.getLbs << {->   &quot;${delegate} pounds&quot; } metaClass.initialize() i =  1 ; println  i // => 1   println  i. class // => java.lang.Integer println  i.lbs // => 1 pounds
To  Expando  or not to  Expando … - global usage (good and bad) - must be aware of context - naming collisions use + aspects/annotations = somewhat finer grained control
Operator Overloading: class  Foo { String meh; public  Foo(String meh) { this .meh = meh} public  String toString() { meh } public  Foo  plus (other) { new  Foo( this .meh + other.toString())   } } a =  new  Foo( &quot;Hello &quot; ) b =  new  Foo( &quot;World&quot; ) c =  &quot;New World&quot; println  a + b  // -> “Hello World” println  a + c  // -> “Hello New World”
Grails/Hibernate Criteria Builder: def  c = Account.createCriteria()  def  results = c {  like( &quot;holderFirstName&quot; ,  &quot;Fred%&quot; )  and {  between( &quot;balance&quot; ,  500 ,  1000 )  eq( &quot;branch&quot; ,  &quot;London&quot; )  }  maxResults( 10 )  order( &quot;holderLastName&quot; ,  &quot;desc&quot; )  }
A Recipe DSL: use(MeasurementCategory) { recipe =  new  Recipe() recipe.ingredients = [ 2 .cups.of( &quot;Milk&quot; ), 2 .tsps.of( &quot;Chocolate Milk Mix&quot; ) ] recipe.steps { with( &quot;drinking glass&quot; ) { add( &quot;Chocolate Milk Mix&quot; ) stirIn( &quot;Milk&quot; ) } } }
Domain Specific Language syntax approaches the problem understanding++
Three things that always matter: Naming Structure Context } Meaning
Thank You! slides at:   http://blog.secosoft.net

Os Secoske

  • 1.
    Implementing DSLs inGroovy Matt Secoske http://blog.secosoft.net http://objectpartners.com
  • 2.
  • 3.
    D omain S pecific L anguage: A language that clearly expresses the ideas of a particular domain
  • 4.
    aka: fluent /humane interfaces problem oriented languages little / mini languages macros (as in Excel)
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
    A DSL isjust another tool in our toolbox. It’s a jig to help us build our applications. picture copyrighted @2007 Pat Warner, http://www.patwarner.com
  • 11.
  • 12.
    bring the syntaxcloser to the domain (the experts language)
  • 13.
    mrMustard .didIt( Murder.of( mrGreen )) .in( theKitchen ) .with( theCandleStick )
  • 14.
  • 15.
    class CreateQuizComments <ActiveRecord::Migration def self.up create_table :quiz_comments do |t| t.column :name, :string t.column :email, :string t.column :url, :string t.column :comment, :text end end def self.down drop_table :quiz_comments end end
  • 16.
    Goal: make the problem easier to understand
  • 17.
    (My) Ultimate Goal: Be able to give the code to the experts, and have them use it
  • 18.
    Types of DSLs:- Internal - External - Functional - Object Oriented
  • 19.
    Internal DSLs - extending a language - can be whatever you want - within the laws of the base language
  • 20.
  • 21.
    A Compelling Story:- Much of the flexibility of Ruby - Familiar Java idioms and syntax - Is focused on the JVM - and Java “pain points”
  • 22.
    Groovy has goodDSL support: - introspection / reflection (ability to parse(itself)) Meta-Object Protocol - syntactic sugar Builders Duck Typing Categories ExpandoMetaClass Named Parameters Operator Overloading
  • 23.
    Introspection / Reflection- Access to the AST at Class loading time - Can run/load code at any time
  • 24.
    class MyGroovy extends GroovyClassLoader { protected CompilationUnit createCompilationUnit(CompilerConfiguration config, CodeSource source) { CompilationUnit cu = super .createCompilationUnit(config, source); cu.addPhaseOperation( new PrimaryClassNodeOperation() { public void call (SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { String name = classNode.getName(); if (name.endsWith( &quot;Foo&quot; )) { BlockStatement code = new BlockStatement(); classNode.addMethod( ”bar&quot; ,Opcodes.ACC_PUBLIC, null , null , null ,code); } } },Phases.CONVERSION); return cu; } } Adapted from blackdrag’s blog: http://blackdragsview.blogspot.com/2006/11/chitchat-with-groovy-compiler.html
  • 25.
    Meta Object Protocol - Determines what’s running at Runtime - add/modify/intercept methods, properties MetaClass Class getFoo() doesntExistOnClass() Other Code
  • 26.
  • 27.
    Duck typing :flexible code if it walks like a duck… and it quacks like a duck… its probably a …. a = ”a string&quot; println a. class // => class java.lang.String a = 1 println a. class // => class java.lang.Integer
  • 28.
    builders : structureddata swing = new SwingBuilder() frame = swing.frame( title: 'Life' ) { vbox { panel( id: 'canvas' ) { vstrut(height: 300 ) hstrut(width: 300 ) } hbox { glue() button( text: 'Next' , actionPerformed:{evolve()}) } }} frame.pack() frame.show()
  • 29.
    Using “ use” (Groovy Categories) class MeasureCategory { static Measurement getLbs( final Integer self) { new Measurement(self, Measurement.Pounds) } } aClosure = {-> println 4 .lbs.of( &quot;Sugar&quot; ) } use (MeasureCategory. class ) { println 1 .lbs.of( &quot;Carrots&quot; ) // -> 1 Pounds of Carrots aClosure. call () // -> 4 Pounds of Sugar }
  • 30.
    Using ExpandoMetaClass:metaClass = new ExpandoMetaClass(Integer. class , true ) metaClass.getLbs << {-> &quot;${delegate} pounds&quot; } metaClass.initialize() i = 1 ; println i // => 1 println i. class // => java.lang.Integer println i.lbs // => 1 pounds
  • 31.
    Using ExpandoMetaClass:metaClass = new ExpandoMetaClass(Integer. class , true ) metaClass.getLbs << {-> &quot;${delegate} pounds&quot; } metaClass.initialize() i = 1 ; println i // => 1 println i. class // => java.lang.Integer println i.lbs // => 1 pounds
  • 32.
    To Expando or not to Expando … - global usage (good and bad) - must be aware of context - naming collisions use + aspects/annotations = somewhat finer grained control
  • 33.
    Operator Overloading: class Foo { String meh; public Foo(String meh) { this .meh = meh} public String toString() { meh } public Foo plus (other) { new Foo( this .meh + other.toString()) } } a = new Foo( &quot;Hello &quot; ) b = new Foo( &quot;World&quot; ) c = &quot;New World&quot; println a + b // -> “Hello World” println a + c // -> “Hello New World”
  • 34.
    Grails/Hibernate Criteria Builder:def c = Account.createCriteria() def results = c { like( &quot;holderFirstName&quot; , &quot;Fred%&quot; ) and { between( &quot;balance&quot; , 500 , 1000 ) eq( &quot;branch&quot; , &quot;London&quot; ) } maxResults( 10 ) order( &quot;holderLastName&quot; , &quot;desc&quot; ) }
  • 35.
    A Recipe DSL:use(MeasurementCategory) { recipe = new Recipe() recipe.ingredients = [ 2 .cups.of( &quot;Milk&quot; ), 2 .tsps.of( &quot;Chocolate Milk Mix&quot; ) ] recipe.steps { with( &quot;drinking glass&quot; ) { add( &quot;Chocolate Milk Mix&quot; ) stirIn( &quot;Milk&quot; ) } } }
  • 36.
    Domain Specific Languagesyntax approaches the problem understanding++
  • 37.
    Three things thatalways matter: Naming Structure Context } Meaning
  • 38.
    Thank You! slidesat: http://blog.secosoft.net