Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

awesome groovy

17,689 views

Published on

Published in: Software
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

awesome groovy

  1. 1. Dr Paul King Groovy Lead for Object Computing Inc. @paulk_asert http:/slideshare.net/paulk_asert/awesome-groovy https://github.com/paulk-asert/awesome-groovy Apache Groovy: The Awesome Parts FRIEND OF GROOVY
  2. 2. Part 1: Introducing Groovy?
  3. 3. What is Groovy? 4 Groovy = Java – boiler plate code + closures (1st class FP) + extensible type system + runtime & compile-time metaprogramming + flexible language grammar (DSLs) + scripting + GDK library “Groovy is like a super version of Java. It leverages Java features but adds productivity features and provides great flexibility and extensibility.”
  4. 4. Why Groovy? Java
  5. 5. Why Groovy? Too verbose Support functional programming Better concurrency Evolves too slowly I need feature XXX Too complex for some situations More flexibility Java
  6. 6. Why Groovy? Groovy Make it simplerMake it dynamic Support simple scripting
  7. 7. Why Groovy? Groovy Make it simplerMake it dynamic Support simple scripting Java integration
  8. 8. Why Groovy? Groovy Make it simplerMake it dynamic Support simple scripting Good IDE support Java integration
  9. 9. Why Groovy? Groovy Make it simplerMake it dynamic Support simple scripting Good IDE support Custom features Java integration
  10. 10. Why Groovy? Groovy Make it simplerMake it dynamic Support simple scripting Support concurrency Good IDE support Custom features Java integration
  11. 11. Why Groovy? Groovy Make it simplerMake it dynamic Support simple scripting Support functional style Support concurrency Good IDE support Custom features Java integration
  12. 12. Java code for list manipulation import java.util.List; import java.util.ArrayList; class Main { private List keepShorterThan(List strings, int length) { List result = new ArrayList(); for (int i = 0; i < strings.size(); i++) { String s = (String) strings.get(i); if (s.length() < length) { result.add(s); } } return result; } public static void main(String[] args) { List names = new ArrayList(); names.add("Ted"); names.add("Fred"); names.add("Jed"); names.add("Ned"); System.out.println(names); Main m = new Main(); List shortNames = m.keepShorterThan(names, 4); System.out.println(shortNames.size()); for (int i = 0; i < shortNames.size(); i++) { String s = (String) shortNames.get(i); System.out.println(s); } } }
  13. 13. Groovy code for list manipulation import java.util.List; import java.util.ArrayList; class Main { private List keepShorterThan(List strings, int length) { List result = new ArrayList(); for (int i = 0; i < strings.size(); i++) { String s = (String) strings.get(i); if (s.length() < length) { result.add(s); } } return result; } public static void main(String[] args) { List names = new ArrayList(); names.add("Ted"); names.add("Fred"); names.add("Jed"); names.add("Ned"); System.out.println(names); Main m = new Main(); List shortNames = m.keepShorterThan(names, 4); System.out.println(shortNames.size()); for (int i = 0; i < shortNames.size(); i++) { String s = (String) shortNames.get(i); System.out.println(s); } } } Rename Main.java to Main.groovy
  14. 14. Some Java Boilerplate identified import java.util.List; import java.util.ArrayList; class Main { private List keepShorterThan(List strings, int length) { List result = new ArrayList(); for (int i = 0; i < strings.size(); i++) { String s = (String) strings.get(i); if (s.length() < length) { result.add(s); } } return result; } public static void main(String[] args) { List names = new ArrayList(); names.add("Ted"); names.add("Fred"); names.add("Jed"); names.add("Ned"); System.out.println(names); Main m = new Main(); List shortNames = m.keepShorterThan(names, 4); System.out.println(shortNames.size()); for (int i = 0; i < shortNames.size(); i++) { String s = (String) shortNames.get(i); System.out.println(s); } } } Are the semicolons needed? And shouldn’t we us more modern list notation? Why not import common libraries? Do we need the static types? Must we always have a main method and class definition? How about improved consistency?
  15. 15. Java Boilerplate removed def keepShorterThan(strings, length) { def result = new ArrayList() for (s in strings) { if (s.size() < length) { result.add(s) } } return result } names = new ArrayList() names.add("Ted"); names.add("Fred") names.add("Jed"); names.add("Ned") System.out.println(names) shortNames = keepShorterThan(names, 4) System.out.println(shortNames.size()) for (s in shortNames) { System.out.println(s) }
  16. 16. More Java Boilerplate identified def keepShorterThan(strings, length) { def result = new ArrayList() for (s in strings) { if (s.size() < length) { result.add(s) } } return result } names = new ArrayList() names.add("Ted"); names.add("Fred") names.add("Jed"); names.add("Ned") System.out.println(names) shortNames = keepShorterThan(names, 4) System.out.println(shortNames.size()) for (s in shortNames) { System.out.println(s) } Shouldn’t we have special notation for lists? And special facilities for list processing? Is ‘return’ needed at end? Is the method now needed? Simplify common methods? Remove unambiguous brackets?
  17. 17. Boilerplate removed = nicer Groovy version names = ["Ted", "Fred", "Jed", "Ned"] println names shortNames = names.findAll{ it.size() < 4 } println shortNames.size() shortNames.each{ println it } ["Ted", "Fred", "Jed", "Ned"] 3 Ted Jed Ned Output:
  18. 18. Or Groovy DSL version if required given the names "Ted", "Fred", "Jed" and "Ned" display all the names display the number of names having size less than 4 display the names having size less than 4 // plus a DSL implementation
  19. 19. Or Groovy DSL version if required given the names "Ted", "Fred", "Jed" and "Ned" display all the names display the number of names having size less than 4 display the names having size less than 4 names = [] def of, having, less def given(_the) { [names:{ Object[] ns -> names.addAll(ns) [and: { n -> names += n }] }] } def the = [ number: { _of -> [names: { _having -> [size: { _less -> [than: { size -> println names.findAll{ it.size() < size }.size() }]}] }] }, names: { _having -> [size: { _less -> [than: { size -> names.findAll{ it.size() < size }.each{ println it } }]}] } ] def all = [the: { println it }] def display(arg) { arg }
  20. 20. Or Groovy DSL version if required • Or use GDSL (IntelliJ IDEA) or DSLD (Eclipse) given the names "Ted", "Fred", "Jed" and "Ned" display all the names display the number of names having size less than 4 display the names having size less than 4
  21. 21. Or typed Groovy DSL version if required given the names "Ted", "Fred", "Jed" and "Ned" display all the names display the number of names having size less than 4 display the names having size less than 4 … enum The { the } enum Having { having } enum Of { of } … class DisplayThe { DisplayTheNamesHaving names(Having having) { new DisplayTheNamesHaving() } DisplayTheNumberOf number(Of of) { new DisplayTheNumberOf() } } … // plus 50 lines
  22. 22. Or typed Groovy DSL version if required
  23. 23. Groovy DSL being debugged
  24. 24. Or typed Groovy DSL version if required @TypeChecked(extensions='EdChecker.groovy') def method() { given the names "Ted", "Fred", "Jed" and "Ned" display all the names display the number of names having size less than 4 display the names having size less than 4 }
  25. 25. Or typed Groovy DSL version if required @TypeChecked(extensions='EdChecker.groovy') def method() { given the names "Ted", "Fred", "Jed" and "Ned" display all the names display the number of names having size less than 4 display the names having size less than 4 } afterMethodCall { mc -> mc.arguments.each { if (isConstantExpression(it)) { if (it.value instanceof String && !it.value.endsWith('ed')) { addStaticTypeError("I don't like the name '${it.value}'", mc) } } } } 
  26. 26. @TypeChecked(extensions='EdChecker.groovy') def method() { given the names "Ted", "Fred", "Jed" and "Ned" display all the names display the number of names having size less than 4 display the names having size less than 4 } Or typed Groovy DSL version if required afterMethodCall { mc -> mc.arguments.each { if (isConstantExpression(it)) { if (it.value instanceof String && !it.value.endsWith('ed')) { addStaticTypeError("I don't like the name '${it.value}'", mc) } } } }
  27. 27. @TypeChecked(extensions='EdChecker.groovy') def method() { given the names "Ted", “Mary", "Jed" and “Pete" display all the names display the number of names having size less than 4 display the names having size less than 4 } afterMethodCall { mc -> mc.arguments.each { if (isConstantExpression(it)) { if (it.value instanceof String && !it.value.endsWith('ed')) { addStaticTypeError("I don't like the name '${it.value}'", mc) } } } } Or typed Groovy DSL version if required
  28. 28. @TypeChecked(extensions='EdChecker.groovy') def method() { given the names "Ted", “Mary", "Jed" and “Pete" display all the names display the number of names having size less than 4 display the names having size less than 4 } afterMethodCall { mc -> mc.arguments.each { if (isConstantExpression(it)) { if (it.value instanceof String && !it.value.endsWith('ed')) { addStaticTypeError("I don't like the name '${it.value}'", mc) } } } } Or typed Groovy DSL version if required 
  29. 29. What style of language is Groovy?
  30. 30. Groovy Style • Imperative/OO but somewhat paradigm agnostic •Dynamic, optionally static (gradual extensible typing) • Extensible language features through metaprogramming
  31. 31. Part 2: What makes Groovy Awesome?
  32. 32. What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Gradual typing • Metaprogramming • Domain specific language (DSL) support • Ecosystem • Community/Team Awesome
  33. 33. What makes Groovy Awesome? • Java integration • Builds up on Java • Tight integration • Polyglot friendly • Scripting support • Multiparadigm • Gradual typing • Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team
  34. 34. Java Integration • Groovy is Java’s friend Java Groovy
  35. 35. Java Integration • Standing on the shoulders of Giants • Some limitations inherited but much gained through new releases of Java • Rock solid foundation • Can ease migration to new versions of Java Java Groovy
  36. 36. Java Integration • Seamless integration • IDEs provide cross-language compile, navigation, and refactoring • Arbitrarily mix source language • Drop-in replace any class • Overloaded methods • Syntax alignment • Shared data types Java Groovy
  37. 37. Java Integration • Seamless integration • IDEs provide cross-language compile, navigation, and refactoring • Arbitrarily mix source language • Drop-in replace any class • Overloaded methods • Syntax alignment • Shared data types Java Groovy
  38. 38. Java Integration • Polyglot friendly • Typically integrates well with other languages which integrate with Java • JRuby • Jython • Scala • Frege • Clojure • R through Renjin • JavaScript (Rhino/Nashorn)
  39. 39. Java Integration • Polyglot friendly • JSR-223 scripting to talk to JavaScript import javax.script.ScriptEngineManager def mgr = new ScriptEngineManager() def engine = mgr.getEngineByName('nashorn') assert engine.eval(''' function factorial(n) { if (n == 0) { return 1; } return n * factorial(n - 1); } factorial(4) ''') == 24.0 Java GroovyJavaScript
  40. 40. Java Integration • Polyglot friendly: R integration @GrabResolver('https://nexus.bedatadriven.com/content/groups/public') @Grab('org.renjin:renjin-script-engine:0.7.0-RC7') import javax.script.ScriptEngineManager def mgr = new ScriptEngineManager() def engine = mgr.getEngineByName('Renjin') engine.with { eval ''' factorial <- function(x) { y <- 1 for (i in 1:x) { y <- y * i } return(y) } ''' assert eval('factorial(4)')[0] == 24 } Java GroovyR
  41. 41. Grooscript @Grab('org.grooscript:grooscript:1.2.0') import org.grooscript.GrooScript import javax.script.ScriptEngineManager def groojs = GrooScript.convert ''' def sayHello = { println "Hello ${it}!" } ['Groovy','JavaScript','GrooScript'].each sayHello''', [initialText: 'var console = { log: print }', addGsLib: 'grooscript.min'] new ScriptEngineManager().getEngineByName("nashorn").eval(groojs) Hello Groovy! Hello JavaScript! Hello GrooScript! Jorge Franco, grooscript.org Convert Groovy subset to JavaScript Support: Gradle, Grails, NPM Demos: react.js, node.js, Pollock Converts: Classes, closures, Date primitives, ranges, String/GString Special: builder, templates, gQuery
  42. 42. What makes Groovy Awesome? • Java integration • Builds up on Java • Tight integration • Polyglot friendly • Scripting support • Multiparadigm • Gradual typing • Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team Awesome • Friend of Java • Integration • Good JVM Citizen
  43. 43. What makes Groovy Awesome? • Java integration • Scripting support • Simplified scripting • Separation of concerns • Security sandbox • Multiparadigm • Gradual typing • Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team Awesome
  44. 44. Scripting • Terse format • Isolation • Domain-Specific Languages (DSLs) • Security • Flexibility
  45. 45. Scripting • Leave out surrounding class • Entire source file: public class Script { public static void main(String[] args) { System.out.println("Hello world"); } } println "Hello world"
  46. 46. Scripting • Various approaches supported • GroovyShell • GroovyScriptEngine • GroovyClassLoader • Spring • JSR-223 • Base scripts • allows predefined methods • Binding • allows predefined variables • Customizers • add imports, static imports • lock down AST to a subset • apply any AST transform transparently
  47. 47. Scripting
  48. 48. Scripting
  49. 49. Scripting
  50. 50. Scripting class Robot { void move(dir) { println "robot moved $dir" } } def robot = new Robot()
  51. 51. Scripting import static Direction.* enum Direction { left, right, forward, backward } class Robot { void move(Direction dir) { println "robot moved $dir" } } def robot = new Robot() robot.move left
  52. 52. Scripting move left def robot = new Robot() def binding = new Binding( robot: robot, move: robot.&move, *: Direction.values().collectEntries { [(it.name()): it] } ) def shell = new GroovyShell(binding) shell.evaluate new File(args[0]).text
  53. 53. Scripting move left def robot = new Robot() def binding = new Binding(/* ... */) def imports = new ImportCustomizer().addStaticStars('java.lang.Math') def timeout = new ASTTransformationCustomizer(value: 2, TimedInterrupt) def secure = new SecureASTCustomizer().with { closuresAllowed = false importsWhitelist = [] tokensWhitelist = [ PLUS, MINUS, MULTIPLY, DIVIDE, /*...*/ ] //... } def sysExit = new CompilationCustomizer(CANONICALIZATION) { void call(SourceUnit src, GeneratorContext ctxt, ClassNode cn) { new ClassCodeVisitorSupport() { void visitMethodCallExpression(MethodCallExpression call) { // check for System.exit() } // ... }.visitClass(cn) } } def config = new CompilerConfiguration() config.addCompilationCustomizers(imports, timeout, secure, sysExit) def shell = new GroovyShell(binding, config)
  54. 54. Scripting move left move forward at 3.km/h More examples later
  55. 55. What makes Groovy Awesome? • Java integration • Scripting support • Simplified scripting • Separation of concerns • Security sandbox • Multiparadigm • Gradual typing • Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team Awesome • Simpler scripting • Separation of concerns
  56. 56. What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Object-oriented • Functional, Closures • Logic/Dataflow programming • Gradual typing • Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team
  57. 57. Multiparadigm • Imperative roots from Java • OO abstractions: classes, interfaces, inheritance • Properties • Traits • First class functional support via closures • External libraries for advanced features • Other paradigms via libraries • Logic, dataflow, reactive Awesome
  58. 58. Multiparadigm Imperative/OO Logic programming Functional
  59. 59. Rich OO features • Classes (and scripts, GroovyBeans) • Fields (and properties) • Methods, constructors, multi-methods, named and default parameters • Inheritance (behavior and implementation), interfaces, traits • Type aliases • Packages • Special accessor notation, spread operators, GPath, safe navigation
  60. 60. Traits /** * Things that hop * @author Grace */ trait Hopper { String jump() { "I'm jumping!" } } class Kangaroo implements Hopper {} def skip = new Kangaroo() assert skip.jump() == "I'm jumping!"
  61. 61. Rich functional features • Closures • First-class functions • Higher-order functions • Map, reduce, filter • Mutable & immutable data • Recursion • Lazy & eager evaluation • Advanced FP techniques • Memoization, Trampolines, Composition, Curry • Concurrency
  62. 62. map/filter/reduce @Canonical class Person { String name int age } def people = [ new Person('Peter', 45), new Person('Paul', 35), new Person('Mary', 25) ] assert people .findAll{ it.age < 40 } .collect{ it.name.toUpperCase() } .sort() .join(', ') == 'MARY, PAUL'
  63. 63. map/filter/reduce (+ functional style) @Canonical class Person { String name int age } def people = [ new Person('Peter', 45), new Person('Paul', 35), new Person('Mary', 25) ] assert people .findAll{ it.age < 40 } .collect{ it.name.toUpperCase() } .sort() .join(', ') == 'MARY, PAUL' def young = { person, threshold -> person.age < threshold }.rcurry(40).memoize() assert people .findAll(young) .collect{ it.name.toUpperCase() } .sort() .join(', ') == 'MARY, PAUL'
  64. 64. map/filter/reduce (+ with streams) @Canonical class Person { String name int age } def people = [ new Person('Peter', 45), new Person('Paul', 35), new Person('Mary', 25) ] assert people .findAll{ it.age < 40 } .collect{ it.name.toUpperCase() } .sort() .join(', ') == 'MARY, PAUL' // requires JRE 8 def commaSep = Collectors.joining(", ") assert people.stream() .filter{ it.age < 40 } .map{ it.name.toUpperCase() } .sorted() .collect(commaSep) == 'MARY, PAUL'
  65. 65. Logic/Constraint programming • Logic programming • Declarative style • Logic clauses for example Prolog • Constraint programming • Declarative style similar to logic programming but contain constraints which must be satisfied • Relations between variables are stated as constraints • Not a step or sequence of steps to execute, but rather the properties of a solution to be found
  66. 66. Logic programming example cranes have 2 legs tortoises have 4 legs there are 7 animals there are 20 legs How many of each animal?
  67. 67. Constraint programming example @Grab('org.choco-solver:choco-solver:4.0.4') import org.chocosolver.solver.Model def m = new Model() def totalAnimals = 7 def totalLegs = 20 def numCranes = m.intVar('Cranes', 0, totalAnimals, true) def numTortoises = m.intVar('Tortoises', 0, totalAnimals, true) def numCraneLegs = m.intScaleView(numCranes, 2) def numTortoiseLegs = m.intScaleView(numTortoises, 4) m.arithm(numCranes, '+', numTortoises, '=', totalAnimals).post() m.arithm(numCraneLegs, '+', numTortoiseLegs, '=', totalLegs).post() if (m.solver.solve()) println "$numCranesn$numTortoises" else println "No Solutions" Cranes = 4 Tortoises = 3 ChocoCraneTortoise.groovy See also: JSR-331 Constraint Programming API
  68. 68. Dataflow programming • Declarative style • Emphasizes the movement of data • models programs as a series of connected tasks • A task has explicitly defined inputs and outputs • runs as soon as all of its inputs become available • Inherently parallel
  69. 69. GPars • Library classes and DSL allowing you to handle tasks concurrently: • Data Parallelism map, filter, reduce functionality in parallel with parallel array support • Asynchronous functions extend the Java executor services to enable multi-threaded closure processing • Dataflow Concurrency supports natural shared-memory concurrency model, using single-assignment variables • Actors provide Erlang/Scala-like actors including "remote" actors on other machines • Safe Agents provide a non-blocking mt-safe reference to mutable state; like "agents" in Clojure 72 Awesome
  70. 70. Concurrency challenge • We can analyse the example’s task graph: def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] def a = 5 def b = f1(a) def c = f2(a) def d = f3(c) def f = f4(b, d) assert f == 10
  71. 71. Concurrency challenge • We can analyse the example’s task graph: def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] def a = 5 def b = f1(a) def c = f2(a) def d = f3(c) def f = f4(b, d) assert f == 10 f2 f3 f1 f4 aa b c d f
  72. 72. Concurrency challenge • Manually using asynchronous functions: def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] import static groovyx.gpars.GParsPool.withPool withPool(2) { def a = 5 def futureB = f1.callAsync(a) def c = f2(a) def d = f3(c) def f = f4(futureB.get(), d) assert f == 10 } f2 f3 f1 f4 aa futureB c d f
  73. 73. Concurrency challenge • And with GPars Dataflows: def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] import groovyx.gpars.dataflow.Dataflows import static groovyx.gpars.dataflow.Dataflow.task new Dataflows().with { task { a = 5 } task { b = f1(a) } task { c = f2(a) } task { d = f3(c) } task { f = f4(b, d) } assert f == 10 } f2 f3 f1 f4 aa b c d f
  74. 74. Concurrency challenge • And with GPars Dataflows: def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] import groovyx.gpars.dataflow.Dataflows import static groovyx.gpars.dataflow.Dataflow.task new Dataflows().with { task { f = f4(b, d) } task { d = f3(c) } task { c = f2(a) } task { b = f1(a) } task { a = 5 } assert f == 10 } f2 f3 f1 f4 aa b c d f
  75. 75. What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Object-oriented • Functional, Closures • Logic/dataflow programming • Gradual typing • Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team • Ability to use imperative when needed for speed • Numerous declarative approaches available when it makes sense Awesome
  76. 76. What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Gradual typing • Dynamic strong typing • Static type checking • Extensible • Type inference • Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team
  77. 77. Gradual typing • Dynamic by default • Gradual typing • Static type checking • Extensible type system Awesome
  78. 78. Dynamic vs static 82 def myPets = ['Lassie', 'Skip'] List<String> yourPets = ['Lassie', 'Skip'] shouldFail(ClassCastException) { List ourPets = new Date() }
  79. 79. Dynamic vs static 83 Gradual Type-time? def myPets = ['Lassie', 'Skip'] List<String> yourPets = ['Lassie', 'Skip'] shouldFail(ClassCastException) { List ourPets = new Date() }
  80. 80. Dynamic vs static 84 def myPets = ['Lassie', 'Skip'] List<String> yourPets = ['Lassie', 'Skip'] shouldFail(ClassCastException) { List ourPets = new Date() } def adder = { a, b -> a + b } assert adder(100, 200) == 300 assert adder('X', 'Y') == 'XY'
  81. 81. Dynamic vs static 85 def adder = { a, b -> a + b } assert adder(100, 200) == 300 assert adder('X', 'Y') == 'XY' def myPets = ['Lassie', 'Skip'] List<String> yourPets = ['Lassie', 'Skip'] shouldFail(ClassCastException) { List ourPets = new Date() } Duck-typing
  82. 82. Dynamic vs static 86 def adder = { a, b -> a + b } assert adder(100, 200) == 300 assert adder('X', 'Y') == 'XY' def myPets = ['Lassie', 'Skip'] List<String> yourPets = ['Lassie', 'Skip'] shouldFail(ClassCastException) { List ourPets = new Date() } @TypeChecked def myMethod() { def myPets = ['Lassie', 'Skip'] List yourPets = ['Lassie', 'Skip'] } shouldFail(CompilationFailedException) { assertScript ''' @groovy.transform.TypeChecked def yourMethod() { List ourPets = new Date() } ''' }
  83. 83. Dynamic vs static 87 def adder = { a, b -> a + b } assert adder(100, 200) == 300 assert adder('X', 'Y') == 'XY' def myPets = ['Lassie', 'Skip'] List<String> yourPets = ['Lassie', 'Skip'] shouldFail(ClassCastException) { List ourPets = new Date() } @TypeChecked def myMethod() { def myPets = ['Lassie', 'Skip'] List yourPets = ['Lassie', 'Skip'] } shouldFail(CompilationFailedException) { assertScript ''' @groovy.transform.TypeChecked def yourMethod() { List ourPets = new Date() } ''' } Extensible Inference
  84. 84. Extensible type checking import groovy.transform.TypeChecked import experimental.SprintfTypeChecker @TypeChecked(extensions=SprintfTypeChecker) void main() { sprintf('%s will turn %d on %tF', 'John', new Date(), 21) } [Static type checking] - Parameter types didn't match types expected from the format String: For placeholder 2 [%d] expected 'int' but was 'java.util.Date' For placeholder 3 [%tF] expected 'java.util.Date' but was 'int' sprintf has an Object varargs parameter, hence not normally amenable to further static checking but for constant Strings we can do better using a custom type checking plugin.
  85. 85. Extensible type checking import groovy.sql.Sql import groovy.transform.TypeChecked @TypeChecked(extensions='SQLExtension.groovy') findAthletes(Sql sql) { sql.eachRow('select * frm Athlete') { row -> println row } } SqlTC.groovy: 7: [Static type checking] - SQL query is not valid: net.sf.jsqlparser.JSQLParserException @ line 6, column 15. sql.eachRow('select * frm Athlete') { row -> println row } ^ 1 error
  86. 86. Static compilation import groovy.transform.* class Actor { String firstName, lastName @CompileStatic String getFullName() { "$firstName $lastName" } void makePeace() { new AntBuilder().echo('Peace was never an option') } } def magneto = new Actor(firstName: 'Ian', lastName: 'McKellen') assert magneto.fullName == 'Ian McKellen' magneto.makePeace()
  87. 87. Static compilation speed 0 5 10 15 20 25 30 Fibonacci micro-benchmark
  88. 88. What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Gradual typing • Dynamic strong typing • Static type checking • Extensible • Type inference • Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team • Maximise duck-typing • Minimise noise • yet type inference • Flexibility at runtime • As strict as you want when needed • Fast when needed Awesome
  89. 89. What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Gradual typing • Metaprogramming • Runtime • Compile-time • Macros • Domain Specific Language support • Ecosystem • Community/Team
  90. 90. Runtime metaprogramming • Add instance & static methods, constructors, properties at runtime • Intercept method/property access • Catch missing methods, properties • Used for dynamic builders, aspect-oriented programming, test stubs, mocks & dummies Awesome* * Individual levels of awesomeness may vary
  91. 91. Runtime metaprogramming • Adding methods at runtime assert 'Hello'.reverse() == 'olleH' String.metaClass.swapCase = { delegate.collect{ it in 'A'..'Z' ? it.toLowerCase() : it.toUpperCase() }.join() } assert 'Hello'.swapCase() == 'hELLO'
  92. 92. Runtime metaprogramming • Intercepting methods class Foo { def one() { println "Called one()" } def methodMissing(String name, params) { println "Attempted $name($params)" } } def f = new Foo() f.one() f.two() f.three('Some Arg') Called one() Attempted two([]) Attempted three([Some Arg])
  93. 93. XML Parsing/GPath expressions def xml = ''' <hosts> <host name='MyHost'> <service name='MyMicroService'/> <service name='MyNanoService'/> </host> </hosts> ''' def hosts = new XmlParser().parseText(xml) assert hosts.host.service[0].@name=='MyMicroService'
  94. 94. def xml = ''' <hosts> <host name='MyHost'> <service name='MyMicroService'/> <service name='MyNanoService'/> </host> </hosts> ''' def hosts = new XmlParser().parseText(xml) assert hosts.host.service[0].@name=='MyMicroService' XML Parsing/GPath expressions
  95. 95. Compile-time metaprogramming • Gives you the ability to change the language by augmenting the compilation process Awesome
  96. 96. Compile-time metaprogramming • Modify the program at compile-time @ToString class Person { String first, last } println new Person(first: 'John', last: 'Smith') // => Person(John, Smith)
  97. 97. Compile-time metaprogramming • Modify the program at compile-time class Person { String first, last String toString() { "Person($first, $last)" } } println new Person(first: 'John', last: 'Smith') // => Person(John, Smith)
  98. 98. Parsing Summary public run() ... L1 ALOAD 1 LDC 1 AALOAD ALOAD 0 LDC "Howdy Y'all" INVOKEINTERFACE callCurrent() ARETURN ... println "Howdy Y'all" BlockStatement -> ReturnStatement -> MethodCallExpression -> VariableExpression("this") -> ConstantExpression("println") -> ArgumentListExpression -> ConstantExpression("Howdy Y'all") MyScript.groovy > groovy MyScript.groovy > groovyc MyScript.groovy > groovysh > groovyConsole
  99. 99. • 9 phase compiler – Early stages: read source code and convert into a sparse syntax tree – Middle stages: iteratively build up a more dense and information rich version of the syntax tree – Later stages: check the tree and convert it into byte code/class files Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization Parsing Summary
  100. 100. Parsing - Early Stages Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization @ToString class Greeter { String message = "Howdy Y'all" void greet() { println message } } ClassNode: Greeter MethodNode: greet Property: message type: unresolved(String) AnnotationNode: ToString type: unresolved(ToString) methods: properties: annotations: BlockStatement MethodCall: this.println(message)
  101. 101. Parsing - Middle Stages Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization ClassNode: Greeter MethodNode: greet FieldNode: message type: resolved(String) methods: fields: constructors: ConstructorNode MethodNode: getMessageMethodNode: setMessageMethodNode: toString MethodNode: getMetaClass…
  102. 102. Parsing - Final Stages 106 Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization public greet()V ... L1 ... ALOAD 0 GETFIELD Greeter.message INVOKEINTERFACE callCurrent() POP ...
  103. 103. Immutable Classes • Some Rules • Don’t provide mutators • Ensure that no methods can be overridden o Easiest to make the class final o Or use static factories & non-public constructors • Make all fields final • Make all fields private o Avoid even public immutable constants • Ensure exclusive access to any mutable components o Don’t leak internal references o Defensive copying in and out • Optionally provide equals and hashCode methods • Optionally provide toString method
  104. 104. @Immutable... • Java Immutable Class • As per Joshua Bloch Effective Java public final class Person { private final String first; private final String last; public String getFirst() { return first; } public String getLast() { return last; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((first == null) ? 0 : first.hashCode()); result = prime * result + ((last == null) ? 0 : last.hashCode()); return result; } public Person(String first, String last) { this.first = first; this.last = last; } // ... // ... @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (first == null) { if (other.first != null) return false; } else if (!first.equals(other.first)) return false; if (last == null) { if (other.last != null) return false; } else if (!last.equals(other.last)) return false; return true; } @Override public String toString() { return "Person(first:" + first + ", last:" + last + ")"; } }
  105. 105. ...@Immutable... public final class Person { private final String first; private final String last; public String getFirst() { return first; } public String getLast() { return last; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((first == null) ? 0 : first.hashCode()); result = prime * result + ((last == null) ? 0 : last.hashCode()); return result; } public Person(String first, String last) { this.first = first; this.last = last; } // ... // ... @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (first == null) { if (other.first != null) return false; } else if (!first.equals(other.first)) return false; if (last == null) { if (other.last != null) return false; } else if (!last.equals(other.last)) return false; return true; } @Override public String toString() { return "Person(first:" + first + ", last:" + last + ")"; } } boilerplate• Java Immutable Class • As per Joshua Bloch Effective Java
  106. 106. ...@Immutable @Immutable class Person { String first, last } Awesome
  107. 107. With Macros (Groovy 2.5+) import org.codehaus.groovy.ast.* import org.codehaus.groovy.ast.stmt.* import org.codehaus.groovy.ast.expr.* def ast = new ReturnStatement( new ConstructorCallExpression( ClassHelper.make(Date), ArgumentListExpression.EMPTY_ARGUMENTS ) ) def ast = macro { return new Date() }
  108. 108. With Macros (Groovy 2.5+) Awesome import org.codehaus.groovy.ast.* import org.codehaus.groovy.ast.stmt.* import org.codehaus.groovy.ast.expr.* def ast = new ReturnStatement( new ConstructorCallExpression( ClassHelper.make(Date), ArgumentListExpression.EMPTY_ARGUMENTS ) ) def ast = macro { return new Date() }
  109. 109. With Macros (Groovy 2.5+) • Variations: • Expressions, Statements, Classes • Supports variable substitution, specifying compilation phase def ast = macro { return new Date() }
  110. 110. With Macros (Groovy 2.5+) • AST Matching: • Selective transformations, filtering, testing • Supports placeholders Expression transform(Expression exp) { Expression ref = macro { 1 + 1 } if (ASTMatcher.matches(ref, exp)) { return macro { 2 } } return super.transform(exp) }
  111. 111. Macro method examples class X { String name } class Y { List<X> list } class Z { Y y } Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser. def getName(Z z) { z.y.list[0].name } • nullSafe
  112. 112. Macro method examples class X { String name } class Y { List<X> list } class Z { Y y } def getName(Z z) { z.y.list[0].name } Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser. • nullSafe
  113. 113. Macro method examples class X { String name } class Y { List<X> list } class Z { Y y } def getName(Z z) { z.y.list[0].name } Prone to NPE Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser. • nullSafe
  114. 114. Macro method examples • nullSafe class X { String name } class Y { List<X> list } class Z { Y y } def getName(Z z) { def result = null if (z != null && z.y != null && z.y.list != null && z.y.list[0] != null) { result = z.y.list[0].name } result } Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser.
  115. 115. Macro method examples class X { String name } class Y { List<X> list } class Z { Y y } def getName(Z z) { def result = null if (z != null && z.y != null && z.y.list != null && z.y.list[0] != null) { result = z.y.list[0].name } result } Verbose Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser. • nullSafe
  116. 116. Macro method examples def getName(Z z) { z?.y?.list?[0]?.name } class X { String name } class Y { List<X> list } class Z { Y y } Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser. • nullSafe
  117. 117. class X { String name } class Y { List<X> list } class Z { Y y } Macro method examples Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser. • nullSafe def getName(Z z) { nullSafe(z.y.list[0].name) }
  118. 118. Macro method examples @Macro static Expression nullSafe(MacroContext macroContext, Expression expression) { if (expression instanceof PropertyExpression) { // exp.prop -> exp?.prop … } else if (expression instanceof MethodCallExpression) { // exp.method() -> exp?.method() … } return expression } • nullSafe def getName(Z z) { nullSafe(z.y.list[0].name) }
  119. 119. Macro method examples def fact(num) { return match(num) { when String then fact(num.toInteger()) when(0 | 1) then 1 when 2 then 2 orElse num * fact(num - 1) } } assert fact("5") == 120 See: https://github.com/touchez-du-bois/akatsuki
  120. 120. Macro method examples • Spock inspired @Grab('org.spockframework:spock-core:1.0-groovy-2.4') import spock.lang.Specification class MathSpec extends Specification { def "maximum of two numbers"(int a, int b, int c) { expect: Math.max(a, b) == c where: a | b | c 1 | 3 | 3 7 | 4 | 7 0 | 0 | 0 } }
  121. 121. Macro method examples See: https://github.com/touchez-du-bois/akatsuki doWithData { dowith: assert a + b == c where: a | b || c 1 | 2 || 3 4 | 5 || 9 7 | 8 || 15 }
  122. 122. What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Gradual typing • Metaprogramming • Runtime • Compile-time • Macros • Domain Specific Language support • Ecosystem • Community/Team • Ability to change the language at runtime • Ability to change the language during the compilation process • Macros provide a homogeneous form for writing AST transformations Awesome
  123. 123. Part 2: What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Gradual typing • Metaprogramming • Domain Specific Language support  Command chains • Cranes and tortoises revisited • Ecosystem • Community/Team
  124. 124. Command Chains • Ability to chain method calls without parentheses and dots Awesome
  125. 125. Command Chains • Ability to chain method calls without parentheses and dots move forward at 3.km/h
  126. 126. Command Chains • Ability to chain method calls without parentheses and dots • Equivalent to: move forward at 3.km/h move(forward).at(3.getKm().div(h))
  127. 127. Command chains in DSLs Number.metaClass.getShares = { delegate } Number.metaClass.getDollars = { delegate } String GOOG = 'Google' def sell(int nShares) { [of: { String ticker -> [at: { int price -> println "Sold $nShares $ticker at $$price" }] }] } sell 100.shares of GOOG at 1000.dollars
  128. 128. Command chains in DSLs show = { println it } square_root = { Math.sqrt(it) } def please(action) { [the: { what -> [of: { n -> action(what(n)) }] }] } please show the square_root of 100 // ==> 10.0
  129. 129. Command chains in DSLs show = { println it } square_root = { Math.sqrt(it) } def please(action) { [the: { what -> [of: { n -> action(what(n)) }] }] } please(show).the(square_root).of(100) // ==> 10.0
  130. 130. Command chains in DSLs show = { println it } square_root = { Math.sqrt(it) } def please(action) { [the: { what -> [of: { n -> action(what(n)) }] }] } please(show).the(square_root).of(100) // ==> 10.0 … and again in another language …
  131. 131. Command chains in DSLs // Japanese DSL Object.metaClass.を = Object.metaClass.の = { clos -> clos(delegate) } まず = { it } 表示する = { println it } 平方根 = { Math.sqrt(it) } まず 100 の 平方根 を 表示する // First, show the square root of 100 // => 10.0 // source: http://d.hatena.ne.jp/uehaj/20100919/1284906117 // http://groovyconsole.appspot.com/edit/241001
  132. 132. Part 3: What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Gradual typing • Metaprogramming • Domain Specific Language support • Command chains  Cranes and tortoises revisited • Ecosystem • Community/Team
  133. 133. Logic programming example cranes have 2 legs tortoises have 4 legs there are 7 animals there are 20 legs How many of each animal?
  134. 134. Recall: Constraint programming example @Grab('org.choco-solver:choco-solver:4.0.0') import org.chocosolver.solver.Model def m = new Model() def totalAnimals = 7 def totalLegs = 20 def numCranes = m.intVar('Cranes', 0, totalAnimals, true) def numTortoises = m.intVar('Tortoises', 0, totalAnimals, true) def numCraneLegs = m.intScaleView(numCranes, 2) def numTortoiseLegs = m.intScaleView(numTortoises, 4) m.arithm(numCranes, '+', numTortoises, '=', totalAnimals).post() m.arithm(numCraneLegs, '+', numTortoiseLegs, '=', totalLegs).post() if (m.solver.solve()) println "$numCranesn$numTortoises" else println "No Solutions" Cranes = 4 Tortoises = 3 ChocoCraneTortoise.groovy
  135. 135. Logic programming example dslUntyped/ChocoCraneTortoiseDSL.groovy 50 lines to define the DSL
  136. 136. Logic programming example cranes have 2 legs tortoises have 4 legs there are 7 animals there are 20 legs display solution Cranes 4 Tortoises 3 dslUntyped/ChocoCraneTortoiseDSL.groovy 50 lines to define the DSL
  137. 137. Logic programming example cranes have 2 legs tortoises have 4 legs millipedes have 1000 legs there are 8 animals there are 1020 legs display solution Cranes 4 Tortoises 3 Millipedes 1 dslUntyped/ChocoCraneTortoiseDSL.groovy 50 lines to define the DSL
  138. 138. Logic programming example Crane(2), Tortoise(4), Beetle(6), Centipede(100), Millipede(1000) Solution: Tortoise = 3, Beetle = 23 Solution: Crane = 1, Tortoise = 1, Beetle = 24 Solution: Crane = 25, Centipede = 1 dslTyped/ChocoCraneTortoiseDSL.groovy 80 lines to define the DSL @TypeChecked def main() { animals seen include Crane, Tortoise, Beetle, Centipede leg count is 150 head count is 26 display solution }
  139. 139. @TypeChecked def main() { animals seen include Crane, Tortoise, Beetle, Centipede leg count is 150 head count is 26 display solution } Logic programming example dslTyped/ChocoCraneTortoiseDSL.groovy 80 lines to define the DSL
  140. 140. DSL Type Provider unresolvedVariable { var -> if (!cachedAnimalNames) { def accessKey = '72ddf45a-c751-44c7-9bca-8db3b4513347' // for illustrative purposes, just download xml for a few animals def uid = 'ELEMENT_GLOBAL.2.104550,ELEMENT_GLOBAL.2.105196,ELEMENT_GLOBAL.2.120227' def base = "https://services.natureserve.org/idd/rest/ns/v1.1/globalSpecies" def url = "$base/comprehensive?uid=$uid&NSAccessKeyId=$accessKey" def root = new XmlParser().parse(url) def names = root.globalSpecies.classification.names cachedAnimalNames = names.natureServePrimaryGlobalCommonName*.text()*.replaceAll(' ','') } if (var.name in cachedAnimalNames) { storeType(var, STRING_TYPE) handled = true enclosingClassNode.addField(var.name, 0, STRING_TYPE, new ConstantExpression(var.name)) } }
  141. 141. DSL Type Provider @TypeChecked(extensions='NatureServeAnimalProvider.groovy') def main() { animals seen include SandhillCrane, GopherTortoise, ChihuahuanMillipede leg count is 1020 head count is 8 display solution } Custom checker provider/ChocoCraneTortoiseProvider.groovy 80 lines to define the DSL 25 lines to define the provider
  142. 142. Awesome Awesome Awesome Awesome Ecosystem
  143. 143. Groovy's Awesome Team
  144. 144. Groovy's Awesome Team
  145. 145. Groovy's Awesome Team +100's of others Direct downloads: 2013: approx. 3 million 2014: 4+ million 2015: 12+ million now: 2+ million/month
  146. 146. Groovy's Awesome Team +100's of others just like YOU!
  147. 147. http://www.manning.com/koenig2 Groovy in Action 2nd edition Manning promo code: ctwg3sum (39% off all Manning books) 152

×