Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Lift-off with Groovy 2 and beyond!

Lift-off with Groovy 2 and beyond!

Guillaume Laforge

May 23, 2013
Tweet

More Decks by Guillaume Laforge

Other Decks in Programming

Transcript

  1. GVM

  2. GVM

  3. GVM: Groovy enVironment Manager • The new kid on the

    block • http://gvmtool.net/ — @gvmtool • Manage parallel versions of the various ecosystem projects • Supports... • Groovy, Grails, Griffon, Gradle, Vert.x • On Linux, MacOS, Cygwin, Solaris, FreeBSD
  4. Ratpack • Lightweight Netty-based web app toolkit @GrabResolver("https://oss.jfrog.org/artifactory/repo") @Grab("org.ratpack-framework:ratpack-groovy:0.9.0-SNAPSHOT") import

    static org.ratpackframework.groovy.RatpackScript.ratpack import org.ratpackframework.groovy.templating.TemplateRenderer ratpack { handlers { get { response.send "This is the app root" } get("date") { get(TemplateRenderer).render "date.html" } assets "public" } }
  5. Let’s start the engine with Groovy 2.0 Modularity Java 7:

    Project Coin & invoke dynamic Static type checking & compilation
  6. Groovy modularity • The « groovy-all » weighted... 6 MB

    ! • In addition to the language, we have APIs: • template engine, Ant task scripting, Swing UI builder, JMX builder... • We want a lighter « core » • with APIs in the form of modules • Ability to wire in « extension methods »
  7. The new JARs • One smaller core JAR of 3

    MB • Modules – console – docgenerator – groovydoc – groovysh – ant – bsf – jsr-223 – jmx – sql – swing – servlet – templates – test – testng – json – xml
  8. The new JARs • One smaller core JAR of 3

    MB • Modules – console – docgenerator – groovydoc – groovysh – ant – bsf – jsr-223 – jmx – sql – swing – servlet – templates – test – testng – json – xml
  9. Extension modules • Create your own extension module • contribute

    instance methods package  foo class  StringExtension  {        static  introduces(String  self,  String  name)  {                "Hi  ${name),  I’m  ${self}"        } } //  usage:  "Guillaume".introduces("Cédric")
  10. Extension modules • Create your own extension module • contribute

    instance methods package  foo class  StringExtension  {        static  introduces(String  self,  String  name)  {                "Hi  ${name),  I’m  ${self}"        } } //  usage:  "Guillaume".introduces("Cédric") Same structure as categories
  11. Extension modules • Create your own extension module • contribute

    static methods package  foo class  StaticStringExtension  {        static  hi(String  self)  {                "Hi!"        } } //  usage:  String.hi()
  12. Extension module descriptor • META-INF/ • services/ • org.codehaus.groovy.runtime.ExtensionModule moduleName

     =  stringExtensions moduleVersion  =  1.0 //  comma  separated  list  of  FQN  class  names extensionClasses  =  foo.StringExtension //  comma  separated  list  of  FQN  class  names staticExtensionClasses  =  foo.StaticStringExtension
  13. Binary literals • In addition to decimal, octal and hexa

    • A new binary representation: int  x  =  0b10101111 assert  x  ==  175   byte  aByte  =  0b00100001 assert  aByte  ==  33   int  anInt  =  0b1010000101000101 assert  anInt  ==  41285
  14. Underscores in literals • Use underscores in number literals long

     creditCardNumber  =  1234_5678_9012_3456L long  socialSecurityNumbers  =  999_99_9999L float  monetaryAmount  =  12_345_132.12 long  hexBytes  =  0xFF_EC_DE_5E long  hexWords  =  0xFFEC_DE5E long  maxLong  =  0x7fff_ffff_ffff_ffffL long  alsoMaxLong  =  9_223_372_036_854_775_807L long  bytes  =  0b11010010_01101001_10010100_10010010
  15. Multi-catch exception blocks • A single catch block to catch

    several exceptions at once, rather than duplicating blocks try  {        /*  ...  */ }  catch(IOException  |  NullPointerException  e)  {        /*  un  seul  bloc  */ }
  16. JDK 7 Invoke Dynamic support • A « flag »

    to compile with « indy » • we might propose a backport for JDK < 7 • Avantages • more runtime performance • well... in theory... • In the long term, we might replace • « call site caching » ➔ MethodHandles • « metaclass registry » ➔ ClassValues • and the JIT « inlines » code more easily
  17. Static type checking • Goal: make the compiler « grumpy

    »! • throw errors at compile time • instead of runtime!
  18. Static type checking • A « grumpy » compiler should...

    • say when there’s a typo in a method or variable name • complain when a non-existent method is called • or on bad assignments or use a bad return type
  19. Static type checking • The compiler should infer types... •

    less explicit types and casts • fine grained type inference • « flow typing » • « lowest upper bound »
  20. Static type checking • But the compiler should understand extension

    methods • allows a good level of dynamism, despite the additional restrictions
  21. Typos import  groovy.transform.TypeChecked   void  method()  {}   @TypeChecked  test()

     {        //  Cannot  find  matching  method  metthhoood()        metthhoood()          def  name  =  "Guillaume"        //  variable  naamme  is  undeclared        println  naamme }
  22. Typos import  groovy.transform.TypeChecked   void  method()  {}   @TypeChecked  test()

     {        //  Cannot  find  matching  method  metthhoood()        metthhoood()          def  name  =  "Guillaume"        //  variable  naamme  is  undeclared        println  naamme } Compilation error
  23. Typos import  groovy.transform.TypeChecked   void  method()  {}   @TypeChecked  test()

     {        //  Cannot  find  matching  method  metthhoood()        metthhoood()          def  name  =  "Guillaume"        //  variable  naamme  is  undeclared        println  naamme } Compilation error Compilation error
  24. Typos import  groovy.transform.TypeChecked   void  method()  {}   @TypeChecked  test()

     {        //  Cannot  find  matching  method  metthhoood()        metthhoood()          def  name  =  "Guillaume"        //  variable  naamme  is  undeclared        println  naamme } Compilation error Compilation error Annotation at the method or class level
  25. Wrong variable assignments //  cannot  assign  value  of  type...  to

     variable... int  x  =  new  Object() Set  set  =  new  Object()   String[]  strings  =  ['a','b','c'] int  str  =  strings[0]   //  cannot  find  matching  method  plus() int  i  =  0 i  +=  '1'
  26. Wrong variable assignments //  cannot  assign  value  of  type...  to

     variable... int  x  =  new  Object() Set  set  =  new  Object()   String[]  strings  =  ['a','b','c'] int  str  =  strings[0]   //  cannot  find  matching  method  plus() int  i  =  0 i  +=  '1' Compilation error
  27. Wrong variable assignments //  cannot  assign  value  of  type...  to

     variable... int  x  =  new  Object() Set  set  =  new  Object()   String[]  strings  =  ['a','b','c'] int  str  =  strings[0]   //  cannot  find  matching  method  plus() int  i  =  0 i  +=  '1' Compilation error Compilation error
  28. Wrong variable assignments //  cannot  assign  value  of  type...  to

     variable... int  x  =  new  Object() Set  set  =  new  Object()   String[]  strings  =  ['a','b','c'] int  str  =  strings[0]   //  cannot  find  matching  method  plus() int  i  =  0 i  +=  '1' Compilation error Compilation error Compilation error
  29. Wrong return type //  checks  if/else  branch  return  values @TypeChecked

    int  method()  {        if  (true)  {  'String'  }        else  {  42  } } //  works  for  switch/case  &  try/catch/finally   //  transparent  toString()  implied @TypeChecked String  greeting(String  name)  {        def  sb  =  new  StringBuilder()        sb  <<  "Hi  "  <<  name }
  30. Wrong return type //  checks  if/else  branch  return  values @TypeChecked

    int  method()  {        if  (true)  {  'String'  }        else  {  42  } } //  works  for  switch/case  &  try/catch/finally   //  transparent  toString()  implied @TypeChecked String  greeting(String  name)  {        def  sb  =  new  StringBuilder()        sb  <<  "Hi  "  <<  name } Compilation error
  31. Wrong return type //  checks  if/else  branch  return  values @TypeChecked

    int  method()  {        if  (true)  {  'String'  }        else  {  42  } } //  works  for  switch/case  &  try/catch/finally   //  transparent  toString()  implied @TypeChecked String  greeting(String  name)  {        def  sb  =  new  StringBuilder()        sb  <<  "Hi  "  <<  name } Compilation error In the end, call StringBuilder’s toString()
  32. Type inference @TypeChecked  test()  {        def  name

     =  "    Guillaume    "          //  String  type  infered  (even  inside  GString)        println  "NAME  =  ${name.toUpperCase()}"            //  Groovy  GDK  method  support        //  (GDK  operator  overloading  too)        println  name.trim()          int[]  numbers  =  [1,  2,  3]        //  Element  n  is  an  int        for  (int  n  in  numbers)  {                println  n        } }
  33. Type inference @TypeChecked  test()  {        def  name

     =  "    Guillaume    "          //  String  type  infered  (even  inside  GString)        println  "NAME  =  ${name.toUpperCase()}"            //  Groovy  GDK  method  support        //  (GDK  operator  overloading  too)        println  name.trim()          int[]  numbers  =  [1,  2,  3]        //  Element  n  is  an  int        for  (int  n  in  numbers)  {                println  n        } } Variable optionally typed
  34. Type inference @TypeChecked  test()  {        def  name

     =  "    Guillaume    "          //  String  type  infered  (even  inside  GString)        println  "NAME  =  ${name.toUpperCase()}"            //  Groovy  GDK  method  support        //  (GDK  operator  overloading  too)        println  name.trim()          int[]  numbers  =  [1,  2,  3]        //  Element  n  is  an  int        for  (int  n  in  numbers)  {                println  n        } } Variable optionally typed Type String infered
  35. Type inference @TypeChecked  test()  {        def  name

     =  "    Guillaume    "          //  String  type  infered  (even  inside  GString)        println  "NAME  =  ${name.toUpperCase()}"            //  Groovy  GDK  method  support        //  (GDK  operator  overloading  too)        println  name.trim()          int[]  numbers  =  [1,  2,  3]        //  Element  n  is  an  int        for  (int  n  in  numbers)  {                println  n        } } Variable optionally typed trim() method added dynamically by Groovy Type String infered
  36. Type inference @TypeChecked  test()  {        def  name

     =  "    Guillaume    "          //  String  type  infered  (even  inside  GString)        println  "NAME  =  ${name.toUpperCase()}"            //  Groovy  GDK  method  support        //  (GDK  operator  overloading  too)        println  name.trim()          int[]  numbers  =  [1,  2,  3]        //  Element  n  is  an  int        for  (int  n  in  numbers)  {                println  n        } } Variable optionally typed Array element type inferred trim() method added dynamically by Groovy Type String infered
  37. Mix dynamic & statically checked code @TypeChecked String  greeting(String  name)

     {        //  call  method  with  dynamic  behavior        //  but  with  proper  signature        generateMarkup(name.toUpperCase()) }   //  usual  dynamic  behavior String  generateMarkup(String  name)  {        def  sw  =  new  StringWriter()        new  MarkupBuilder(sw).html  {                body  {                        div  name                }        }        sw.toString() }
  38. Mix dynamic & statically checked code @TypeChecked String  greeting(String  name)

     {        //  call  method  with  dynamic  behavior        //  but  with  proper  signature        generateMarkup(name.toUpperCase()) }   //  usual  dynamic  behavior String  generateMarkup(String  name)  {        def  sw  =  new  StringWriter()        new  MarkupBuilder(sw).html  {                body  {                        div  name                }        }        sw.toString() } Statically checked
  39. Mix dynamic & statically checked code @TypeChecked String  greeting(String  name)

     {        //  call  method  with  dynamic  behavior        //  but  with  proper  signature        generateMarkup(name.toUpperCase()) }   //  usual  dynamic  behavior String  generateMarkup(String  name)  {        def  sw  =  new  StringWriter()        new  MarkupBuilder(sw).html  {                body  {                        div  name                }        }        sw.toString() } Statically checked Dynamic
  40. Instanceof checks @TypeChecked   void  test(Object  val)  {    

       if  (val  instanceof  String)  {                println  val.toUpperCase()        }  else  if  (val  instanceof  Number)  {                println  "X"  *  val.intValue()        } }
  41. Instanceof checks @TypeChecked   void  test(Object  val)  {    

       if  (val  instanceof  String)  {                println  val.toUpperCase()        }  else  if  (val  instanceof  Number)  {                println  "X"  *  val.intValue()        } } No need for casts
  42. Instanceof checks @TypeChecked   void  test(Object  val)  {    

       if  (val  instanceof  String)  {                println  val.toUpperCase()        }  else  if  (val  instanceof  Number)  {                println  "X"  *  val.intValue()        } } No need for casts No need for casts
  43. Instanceof checks @TypeChecked   void  test(Object  val)  {    

       if  (val  instanceof  String)  {                println  val.toUpperCase()        }  else  if  (val  instanceof  Number)  {                println  "X"  *  val.intValue()        } } No need for casts No need for casts Understand GDK’s method: String#multuply(int)
  44. Lowest Upper Bound • The smallest common « super »

    type • might be virtual (« non-denotable ») @TypeChecked  test()  {        //  an  integer  and  a  BigDecimal        return  [1234,  3.14] }
  45. Lowest Upper Bound • The smallest common « super »

    type • might be virtual (« non-denotable ») @TypeChecked  test()  {        //  an  integer  and  a  BigDecimal        return  [1234,  3.14] } Infered type: List<Number & Comparable>
  46. Flow typing • Static type checking « follows » the

    type of values assigned into variables @TypeChecked  test()  {        def  var  =  123                  //  int  infered        int  x  =  var                      //  var  is  an  int        var  =  "123"                      //  assign  a  String  into  var        x  =  var.toInteger()      //  no  cast  needed        var  =  123        x  =  var.toUpperCase()  //  error,  var  is  an  int  ! }
  47. Static type checking and dynamic code • Type checking happens

    at compile-time • @TypeChecked doesn’t change behavior! • do not confound with static compilation • Most dynamic features can’t be checked • metaclass changes, categories... • dynamic variables from the « script binding » • But compile-time metaprogramming is OK • if enough type information is available
  48. No dynamic metaprogramming @TypeChecked   void  test()  {    

       Integer.metaClass.foo  =  {}        123.foo() }
  49. No dynamic metaprogramming @TypeChecked   void  test()  {    

       Integer.metaClass.foo  =  {}        123.foo() } metaClass dynamic property access not allowed
  50. No dynamic metaprogramming @TypeChecked   void  test()  {    

       Integer.metaClass.foo  =  {}        123.foo() } metaClass dynamic property access not allowed Unknown method
  51. Explicit type for closure parameters @TypeChecked  test()  {    

       ["a",  "b",  "c"].collect  {                it.toUpperCase()  //  Not  OK!        } }
  52. Explicit type for closure parameters @TypeChecked  test()  {    

       ["a",  "b",  "c"].collect  {  String  it  -­‐>                it.toUpperCase()  //  OK,  a  String        } }
  53. Explicit type for closure parameters @TypeChecked  test()  {    

       ["a",  "b",  "c"].collect  {  String  it  -­‐>                it.toUpperCase()  //  OK,  a  String        } } Must specify the type explicitely
  54. Explicit type for closure parameters @TypeChecked  test()  {    

       ["a",  "b",  "c"].collect  {  String  it  -­‐>                it.toUpperCase()  //  OK,  a  String        } } Must specify the type explicitely The list could contain anything!
  55. Explicit type for closure parameters @TypeChecked  test(List  myListParameter)  {  

         myListParameter.collect  {  String  it  -­‐>                it.toUpperCase()        } } Must specify the type explicitely The list could contain anything!
  56. Static compilation • Given the code is statically type checked,

    lots of type information was infered... so we can as well compile staticallyment ! • ie. generate the same bytecode as javac • Also interesting when stuck on JDK < 7 to gain performance improvements
  57. Avantages of static compilation • We gain: • type safety

    • thanks to static type checking • the compiler builds upon it • better performance • close to Java’s performance • code immune to « monkey patching » • dynamic metaprogramming can interfere with your framework’s code • smaller generated bytecode
  58. Drawbacks for static compilation • We lose... • Some dynamic

    features • metaclass changes, categories • Method « dynamic dispatch » can differ • but thanks to type inference, it’s as close as «classical» Groovy as possible
  59. Mix statically compiled code with dynamic @CompileStatic String  greeting(String  name)

     {        //  call  method  with  dynamic  behavior        //  but  with  proper  signature        generateMarkup(name.toUpperCase()) }   //  usual  dynamic  behavior String  generateMarkup(String  name)  {        def  sw  =  new  StringWriter()        new  MarkupBuilder(sw).html  {                body  {                        div  name                }        }        sw.toString() }
  60. Mix statically compiled code with dynamic @CompileStatic String  greeting(String  name)

     {        //  call  method  with  dynamic  behavior        //  but  with  proper  signature        generateMarkup(name.toUpperCase()) }   //  usual  dynamic  behavior String  generateMarkup(String  name)  {        def  sw  =  new  StringWriter()        new  MarkupBuilder(sw).html  {                body  {                        div  name                }        }        sw.toString() } Statically compiled
  61. Mix statically compiled code with dynamic @CompileStatic String  greeting(String  name)

     {        //  call  method  with  dynamic  behavior        //  but  with  proper  signature        generateMarkup(name.toUpperCase()) }   //  usual  dynamic  behavior String  generateMarkup(String  name)  {        def  sw  =  new  StringWriter()        new  MarkupBuilder(sw).html  {                body  {                        div  name                }        }        sw.toString() } Statically compiled Dynamic
  62. Mix statically compiled code with dynamic @CompileStatic String  greeting(String  name)

     {        //  call  method  with  dynamic  behavior        //  but  with  proper  signature        generateMarkup(name.toUpperCase()) }   //  usual  dynamic  behavior String  generateMarkup(String  name)  {        def  sw  =  new  StringWriter()        new  MarkupBuilder(sw).html  {                body  {                        div  name                }        }        sw.toString() } Statically compiled Dynamic Call a method with dynamic content
  63. Mix statically compiled code with dynamic @CompileStatic String  greeting(String  name)

     {        //  call  method  with  dynamic  behavior        //  but  with  proper  signature        generateMarkup(name.toUpperCase()) }   //  usual  dynamic  behavior String  generateMarkup(String  name)  {        def  sw  =  new  StringWriter()        new  MarkupBuilder(sw).html  {                body  {                        div  name                }        }        sw.toString() } Statically compiled Dynamic Call a method with dynamic content Method signatures are a contract!
  64. What about performance? • Comparisons between: • Java • Groovy

    • with static compilation — Groovy 2.0 • with primitive type optimizations — Groovy 1.8+ • no optimization — Groovy 1.7
  65. What about performance? Fibonacci Pi (π) quadrature Binary trees Java

    Static compilation Primitive optimizations No prim. optimizations 191 ms 97 ms 3.6 s 197 ms 101 ms 4.3 s 360 ms 111 ms 23.7 s 2590 ms 3220 ms 50.0 s 1.7 1.8 2.x
  66. ...and now, onto Groovy 2.1 Complete Invoke Dynamic support Meta-annotations

    Advanced compiler configuration Type checker extensions
  67. Complete « Invoke Dynamic » support • In Groovy 2.0,

    all method call weren’t going through the « indy » support • only normal method calls • still joint usage of the « call site caching » technique • On JDK 7, with the « indy » JAR, Groovy 2.1 uses « invoke dynamic » everywhere • On JDK < 7, still using « call site caching »
  68. Meta-annotations @Immutable @ToString(excludes  =  ["age"]) @AnnotationCollector @interface  MyAlias  {} @MyAlias

    class  Person  {        String  name        int  age } Collected annotations The collector Your own annotation alias
  69. Meta-annotations @Immutable @ToString(excludes  =  ["age"]) @AnnotationCollector @interface  MyAlias  {} @MyAlias

    class  Person  {        String  name        int  age } Collected annotations The collector Your own annotation alias Use your meta- annotation
  70. @DelegatesTo annotation • Static type checking works fine with a

    certain range of DSLs • « command chains », extension methods... • But less for DSLs using closure delegation • often used by DSLs like in Gradle task  copyTask(type:  Copy)  {        from  'src/main/webapp'        into  'build/explodedWar' }
  71. @DelegatesTo annotation class  ExecSpec  {        void  foo()

    } void  exec(ExecSpec  sp,  Closure  c)  {        c.delegate  =  sp        c() } exec(spec)  {        foo() }
  72. @DelegatesTo annotation class  ExecSpec  {        void  foo()

    } void  exec(ExecSpec  sp,  Closure  c)  {        c.delegate  =  sp        c() } exec(spec)  {        foo() } The static type checker doesn’t know about method foo()
  73. @DelegatesTo annotation class  ExecSpec  {        void  foo()

    } void  exec(ExecSpec  sp,  Closure  c)  {        c.delegate  =  sp        c() } exec(spec)  {        foo() } Annotate with @DelegatesTo(ExecSpec) The static type checker doesn’t know about method foo()
  74. @DelegatesTo annotation • With another delegation strategy void  exec(ExecSpec  sp,

     Closure  c)  {        c.delegate  =  sp        c.resolveStrategy  =  DELEGATE_FIRST        c() }
  75. @DelegatesTo annotation • With another delegation strategy void  exec(ExecSpec  sp,

     Closure  c)  {        c.delegate  =  sp        c.resolveStrategy  =  DELEGATE_FIRST        c() } Annotate with @DelegatesTo(value = ExecSpec, strategy = DELEGATE_FIRST)
  76. @DelegatesTo annotation • Use Target to specify a particular delegatee

    void  exec(ExecSpec  sp,  Closure  c)  {        c.delegate  =  sp        c() }
  77. @DelegatesTo annotation • Use Target to specify a particular delegatee

    void  exec(ExecSpec  sp,  Closure  c)  {        c.delegate  =  sp        c() } @DelegatesTo.Target(‘‘id’’)
  78. @DelegatesTo annotation • Use Target to specify a particular delegatee

    void  exec(ExecSpec  sp,  Closure  c)  {        c.delegate  =  sp        c() } @DelegatesTo.Target(‘‘id’’) @DelegatesTo(target = ‘‘id’’)
  79. @DelegatesTo annotation • Very interesting for DSLs using closure’s delegation

    strategy • Excellent for... • documenting your APIs • the integration within the IDE • code completion, code navigation • works well with static type checking and static compilation
  80. Extend the static type checker • Extend the type checker

    to make it smarter! • even smarter than Java’s! :-) • By creating your own extension @TypeChecked(extensions  =                            'MyExtension.groovy') void  exec()  {        //  code  to  be  further  checked... }
  81. Extend the static type checker • Extend the type checker

    to make it smarter! • even smarter than Java’s! :-) • By creating your own extension @TypeChecked(extensions  =                            'MyExtension.groovy') void  exec()  {        //  code  to  be  further  checked... } We could use a meta-annotation
  82. • Help the static type checker when... • impossible to

    infer types • no matching method found • no matching attribute found • on wrong assignments Extend the static type checker
  83. • Your extension has access to an event-oriented API Extend

    the static type checker • onMethodSelection • afterMethodCall • beforeMethodCall • afterVisitMethod • beforeVisitMethod • methodNotFound • unresolvedVariable • unresolvedProperty • unresolvedAttribute • incompatibleAssignment
  84. Extend the static type checker onMethodSelection  {  expr,  method  -­‐>

     ...  } afterMethodCall  {  mc  -­‐>  ...  } unresolvedVariable  {  var  -­‐>  ...  } methodNotFound  {  receiver,  name,  argList,  argTypes,  call  -­‐>  ...  } incompatibleAssignment  {  lhsType,  rhsType,  expr  -­‐>  ...  }
  85. Extend the static type checker onMethodSelection  {  expr,  method  -­‐>

     ...  } afterMethodCall  {  mc  -­‐>  ...  } unresolvedVariable  {  var  -­‐>  ...  } methodNotFound  {  receiver,  name,  argList,  argTypes,  call  -­‐>  ...  } incompatibleAssignment  {  lhsType,  rhsType,  expr  -­‐>  ...  } MyExtension.groovy
  86. Extend the static type checker onMethodSelection  {  expr,  method  -­‐>

     ...  } afterMethodCall  {  mc  -­‐>  ...  } unresolvedVariable  {  var  -­‐>  ...  } methodNotFound  {  receiver,  name,  argList,  argTypes,  call  -­‐>  ...  } incompatibleAssignment  {  lhsType,  rhsType,  expr  -­‐>  ...  } MyExtension.groovy Learn your Groovy AST!
  87. Extend the static type checker onMethodSelection  {  expr,  method  -­‐>

     ...  } afterMethodCall  {  mc  -­‐>  ...  } unresolvedVariable  {  var  -­‐>  ...  } methodNotFound  {  receiver,  name,  argList,  argTypes,  call  -­‐>  ...  } incompatibleAssignment  {  lhsType,  rhsType,  expr  -­‐>  ...  } MyExtension.groovy Learn your Groovy AST! No need to be pre-compiled
  88. • A few examples... • Check that a string is

    a valid SQL query • Checked the arguments and types of sprintf() method calls so they match the pattern Extend the static type checker
  89. Compiler customization • Groovy 1.8 introduced « customizers » •

    add imports transparently • apply AST transformations by default • filter / secure scripts • With the « static type checker » and « static compilation », we were asked if we could apply them by default
  90. Compiler customization • New options • --basescript to define a

    base script class for your scripts • --configscript to indicate a script to configure the CompilerConfiguration object
  91. Compiler customization • Add the @ToString AST transformation • import

     groovy.transform.ToString import  org.codehaus.groovy.control.customizers              .ASTTransformationCustomizer configuration.addCompilationCustomizer(        new  ASTTransformationCustomizer(ToString) )
  92. Compiler customization • Add the @ToString AST transformation • import

     groovy.transform.ToString import  org.codehaus.groovy.control.customizers              .ASTTransformationCustomizer configuration.addCompilationCustomizer(        new  ASTTransformationCustomizer(ToString) ) CompilerConfiguration instance, injected by default
  93. Compiler customization • A small DSL to configure the customization

    configuration.customizers  {        //  apply  to  MyBean.groovy        source(basename:  'MyBean')  {                ast(ToString)        } }
  94. Compiler customization • A small DSL to configure the customization

    configuration.customizers  {        //  apply  to  MyBean.groovy        source(basename:  'MyBean')  {                ast(ToString)        } } configuration.customizers  {        //  apply  to  *.gbean  files        source(extension:  '.gbean')  {                ast(ToString)        } }
  95. Compiler customization • A small DSL to configure the customization

    configuration.customizers  {        //  apply  to  MyBean.groovy        source(basename:  'MyBean')  {                ast(ToString)        } } configuration.customizers  {        //  apply  to  *.gbean  files        source(extension:  '.gbean')  {                ast(ToString)        } } configuration.customizers  {        //  custom  filter  logic        source(unitValidator:  {  unit  -­‐>  ...  })  {                ast(ToString)                imports  {                        staticStar  'java.lang.Math'                }        } }
  96. And what’s next? Groovy 2.2 & 3 ! New «

    MOP » New Grammar with Antlr v4 Java 8 Lambdas support
  97. A few words about the roadmap 2014 2014 2013 2012

    Groovy 2.1 Groovy 2.0 Groovy 2.0 Groovy 2.2 Groovy 3.0
  98. A few words about the roadmap 2014 2014 2013 2012

    Groovy 2.1 Groovy 2.0 Groovy 2.0 Groovy 2.2 Groovy 3.0
  99. A few words about the roadmap 2014 2014 2013 2012

    Groovy 2.1 Groovy 2.0 Groovy 2.0 Groovy 2.2 Groovy 3.0
  100. DelegatingScript base script class • Special base script class to

    delegate method calls and property accesses to a delegatee
  101. DelegatingScript base script class • Special base script class to

    delegate method calls and property accesses to a delegatee Handy for DSLs!
  102. DelegatingScript base script class • Special base script class to

    delegate method calls and property accesses to a delegatee Handy for DSLs! name  =  "Guillaume" sayHi()
  103. DelegatingScript base script class • Special base script class to

    delegate method calls and property accesses to a delegatee class  Person  {        String  name        void  sayHi()  {                  println  "Hi  $name"          } } Handy for DSLs! name  =  "Guillaume" sayHi()
  104. DelegatingScript base script class • Special base script class to

    delegate method calls and property accesses to a delegatee class  Person  {        String  name        void  sayHi()  {                  println  "Hi  $name"          } } Handy for DSLs! name  =  "Guillaume" sayHi() Use Person’s name property
  105. DelegatingScript base script class • Special base script class to

    delegate method calls and property accesses to a delegatee class  Person  {        String  name        void  sayHi()  {                  println  "Hi  $name"          } } Handy for DSLs! name  =  "Guillaume" sayHi() Use Person’s name property Call Person#sayHi()
  106. DelegatingScript base script class • Integration example: def  cc  =

     new  CompilerConfiguration() cc.scriptBaseClass  =  DelegatingScript.class.name def  sh  =  new  GroovyShell(cc) def  script  =  sh.parse(file) def  p  =  new  Person() script.setDelegate(p) script.run() assert  p.name  ==  "Guillaume"
  107. DelegatingScript base script class • Integration example: def  cc  =

     new  CompilerConfiguration() cc.scriptBaseClass  =  DelegatingScript.class.name def  sh  =  new  GroovyShell(cc) def  script  =  sh.parse(file) def  p  =  new  Person() script.setDelegate(p) script.run() assert  p.name  ==  "Guillaume" Specify DelegatingScript base class
  108. DelegatingScript base script class • Integration example: def  cc  =

     new  CompilerConfiguration() cc.scriptBaseClass  =  DelegatingScript.class.name def  sh  =  new  GroovyShell(cc) def  script  =  sh.parse(file) def  p  =  new  Person() script.setDelegate(p) script.run() assert  p.name  ==  "Guillaume" Specify DelegatingScript base class Parse the script
  109. DelegatingScript base script class • Integration example: def  cc  =

     new  CompilerConfiguration() cc.scriptBaseClass  =  DelegatingScript.class.name def  sh  =  new  GroovyShell(cc) def  script  =  sh.parse(file) def  p  =  new  Person() script.setDelegate(p) script.run() assert  p.name  ==  "Guillaume" Specify DelegatingScript base class Parse the script Define the delegate
  110. DelegatingScript base script class • Integration example: def  cc  =

     new  CompilerConfiguration() cc.scriptBaseClass  =  DelegatingScript.class.name def  sh  =  new  GroovyShell(cc) def  script  =  sh.parse(file) def  p  =  new  Person() script.setDelegate(p) script.run() assert  p.name  ==  "Guillaume" Specify DelegatingScript base class Parse the script Define the delegate Run the script
  111. DelegatingScript base script class • Integration example: def  cc  =

     new  CompilerConfiguration() cc.scriptBaseClass  =  DelegatingScript.class.name def  sh  =  new  GroovyShell(cc) def  script  =  sh.parse(file) def  p  =  new  Person() script.setDelegate(p) script.run() assert  p.name  ==  "Guillaume" Specify DelegatingScript base class Parse the script Define the delegate Run the script Be Happy!
  112. @Cached transformation • Like @Lazy fields, but applied to methods

    • including lazy computation @Cached  createService()  {  ...  }
  113. @Cached transformation • Like @Lazy fields, but applied to methods

    • including lazy computation @Cached  createService()  {  ...  } However, result is cached only on first invocation, but could be expanded to work more like Closure’s memoize() to cache return values depending on different input parameter values
  114. @DelegatesTo with generic type tokens @groovy.transform.InheritConstructors class  MyList  extends  LinkedList<String>

     {} public  <T>  Object  map(            @DelegatesTo.Target  List<T>  target,              @DelegatesTo(genericTypeIndex  =  0)  Closure  arg)  {        arg.delegate  =  target.join('')        arg() } def  test()  {        map(new  MyList(['f',  'o',  'o']))  {                assert  toUpperCase()  ==  'FOO'        } }
  115. @DelegatesTo with generic type tokens @groovy.transform.InheritConstructors class  MyList  extends  LinkedList<String>

     {} public  <T>  Object  map(            @DelegatesTo.Target  List<T>  target,              @DelegatesTo(genericTypeIndex  =  0)  Closure  arg)  {        arg.delegate  =  target.join('')        arg() } def  test()  {        map(new  MyList(['f',  'o',  'o']))  {                assert  toUpperCase()  ==  'FOO'        } } We want to delegate to the token type T
  116. @DelegatesTo with generic type tokens @groovy.transform.InheritConstructors class  MyList  extends  LinkedList<String>

     {} public  <T>  Object  map(            @DelegatesTo.Target  List<T>  target,              @DelegatesTo(genericTypeIndex  =  0)  Closure  arg)  {        arg.delegate  =  target.join('')        arg() } def  test()  {        map(new  MyList(['f',  'o',  'o']))  {                assert  toUpperCase()  ==  'FOO'        } } We want to delegate to the token type T We define the index of the type token
  117. @DelegatesTo with generic type tokens @groovy.transform.InheritConstructors class  MyList  extends  LinkedList<String>

     {} public  <T>  Object  map(            @DelegatesTo.Target  List<T>  target,              @DelegatesTo(genericTypeIndex  =  0)  Closure  arg)  {        arg.delegate  =  target.join('')        arg() } def  test()  {        map(new  MyList(['f',  'o',  'o']))  {                assert  toUpperCase()  ==  'FOO'        } } We want to delegate to the token type T We define the index of the type token This is String’s toUpperCase() method
  118. Miscelanous improvements • Precompiled type checking extensions • Further tweaks

    to Groovysh with code completion, better error reporting... • Better syntax highlighting in Groovy Console • Various dependency upgrades (Gradle, Ant) @TypeChecked(extensions  =  'fqn.MyExtension')
  119. Additional GDK methods... • groupBy() on arrays • combinations(Closure) •

    collectMany() on Iterables • JsonSlurper’s parse(File) and parse(URL) assert  [[2,  3],  [4,  5,  6]]                    .combinations  {  x,  y  -­‐>  x*y  }  ==                                            [8,  12,  10,  15,  12,  18]
  120. Implicit closure coercion interface  Predicate<T>  {        boolean

     test(T  t) } List<T>  filter(Predicate<T>  p)
  121. Implicit closure coercion interface  Predicate<T>  {        boolean

     test(T  t) } List<T>  filter(Predicate<T>  p) Given a predicate & a List method to filter according to that predicate...
  122. Implicit closure coercion interface  Predicate<T>  {        boolean

     test(T  t) } List<T>  filter(Predicate<T>  p) list.filter((p)  -­‐>  p.age  >  18) Given a predicate & a List method to filter according to that predicate...
  123. Implicit closure coercion interface  Predicate<T>  {        boolean

     test(T  t) } List<T>  filter(Predicate<T>  p) list.filter((p)  -­‐>  p.age  >  18) Given a predicate & a List method to filter according to that predicate... Java 8 lambdas can be more concise than Groovy!
  124. Implicit closure coercion interface  Predicate<T>  {        boolean

     test(T  t) } List<T>  filter(Predicate<T>  p) list.filter((p)  -­‐>  p.age  >  18) list.filter({  it.age  >  18  }  as  Predicate) Given a predicate & a List method to filter according to that predicate... Java 8 lambdas can be more concise than Groovy!
  125. Implicit closure coercion interface  Predicate<T>  {        boolean

     test(T  t) } List<T>  filter(Predicate<T>  p) list.filter((p)  -­‐>  p.age  >  18) list.filter  {  it.age  >  18  }   Given a predicate & a List method to filter according to that predicate... Java 8 lambdas can be more concise than Groovy!
  126. Implicit closure coercion interface  Predicate<T>  {        boolean

     test(T  t) } List<T>  filter(Predicate<T>  p) list.filter((p)  -­‐>  p.age  >  18) list.filter  {  it.age  >  18  }   Given a predicate & a List method to filter according to that predicate... Java 8 lambdas can be more concise than Groovy! When no ambiguity, make coercion implicit!
  127. Implicit closure coercion interface  Predicate<T>  {        boolean

     test(T  t) } List<T>  filter(Predicate<T>  p) list.filter((p)  -­‐>  p.age  >  18) list.filter  {  it.age  >  18  }   Given a predicate & a List method to filter according to that predicate... Java 8 lambdas can be more concise than Groovy! When no ambiguity, make coercion implicit! And go beyond Java, by making it work on abstract classes as well
  128. Summary • A very rich and blossoming ecosystem • Groovy

    2.0 • more modular • a static theme • static type checking • static compilation • JDK 7 theme • Invoke Dynamic support • Project Coin syntax enhancements
  129. Summary • Groovy 2.1 • Invoke Dynamic support completed •

    @DelegatesTo annotation • type checker extensions for DSLs • meta-annotations • Groovy 2.2 • @Cached transformation • DelegatingScript for script DSLs • groovysh improvements
  130. Summary • Groovy 3 • a new MOP (Meta-Object Protocol)

    • a new grammar with Antlr v4 • the support of JDK 8 lambdas
  131. Image credits • lift-off: http://www.wpclipart.com/space/ships/space_shuttle/Space_Shuttle_liftoff.png • cerisier: http://wallpaperswide.com/cherry_blossom_3-wallpapers.html • NKOTB:

    http://images1.fanpop.com/images/photos/2300000/nkotb-new-kids-on-the-block-2314664-1280-960.jpg • lunar module: http://www.clavius.org/img/lm-diag.gif • tomates: http://www.infoescola.com/wp-content/uploads/2011/01/tomate.jpg • patates: http://toooof.free.fr/blogs/captainslip/screenshots/pommes_de_terre.jpg • coins: http://www.coins-jewelry.com/c22.png • more coins: http://diamond-center.co.il/upload/articles/gold-coins1.jpg • binary: http://okletsgo.co.uk/img/binary.jpg • grumpy: https://si0.twimg.com/profile_images/3115998027/b47c180a703a5ffa7d1437a66f545dc0.jpeg • singe: http://static.ddmcdn.com/gif/how-to-draw-animals-31.jpg • warning: http://th07.deviantart.net/fs71/PRE/i/2012/261/8/6/warning_gangnam_style_zone_by_untoucheddesigns-d5f6bal.png • coyote: http://nittygriddy.com/wp-content/uploads/2011/01/Wiley-Coyote-Help.jpg • ring: http://img.banggood.com/images/upload/2012/limin/SKU028431_11.JPG • magnifying glass: http://www.renders-graphiques.fr/image/upload/normal/loupe.png • work in progress: http://www.sbscompany.org/multimedia/immagini/work-in-progress.png • tab key: http://www.meganga.com/wp-content/uploads/2012/03/Tab-Key-Word-Tutorials.jpg • chronomètre: http://www.moineau-instruments.com/59-thickbox/chronometre-mecanique-1-10-t15-mn-2-fonctions.jpg • that’s all folks: http://4.bp.blogspot.com/-wJxosualm48/T4M_spcUUjI/AAAAAAAAB8E/njfLjNZQdsc/s1600/thats-all-folks.jpg • MOP: http://imagethumbnails.milo.com/024/913/894/trimmed/24913521_25989894_trimmed.jpg • grammar: http://edudemic.com/wp-content/uploads/2012/11/connected-learner-grammar.jpg