Apache Groovy: The Awesome Parts © ASERT 2006-2016
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
© ASERT 2006-2016
What is Groovy?
© ASERT 2006-2016
“Groovy is like a super version of Java. It leverages Java features but adds productivity features and provides great flexibility and extensibility.” Groovy = Java – + + +
boiler plate code closures (1st class FP) extensible type system runtime & compile-time metaprogramming, macros + flexible grammar (DSLs) + scripting & GDK library
© ASERT 2006-2016
Why Groovy?
© ASERT 2006-2016
Java
Why Groovy? Too verbose
© ASERT 2006-2016
Too complex for some situations Better concurrency
Support functional programming
Java More flexibility
I need feature XXX
Evolves too slowly
Why Groovy?
Make it dynamic
Make it simpler
© ASERT 2006-2016
Groovy
Support simple scripting
Why Groovy?
Make it dynamic
Make it simpler
© ASERT 2006-2016
Groovy Java integration
Support simple scripting
Why Groovy?
Make it dynamic
Make it simpler
© ASERT 2006-2016
Groovy Java integration
Support simple scripting Good IDE support
Why Groovy?
Make it dynamic
Make it simpler
© ASERT 2006-2016
Groovy Custom features
Java integration
Support simple scripting Good IDE support
Why Groovy?
Make it dynamic
Make it simpler
© ASERT 2006-2016
Groovy Custom features
Java integration
Support simple scripting Good IDE support Support concurrency
Why Groovy?
Make it dynamic © ASERT 2006-2016
Support functional style Custom features
Make it simpler
Groovy Java integration
Support simple scripting Good IDE support Support concurrency
© ASERT 2006-2016
Java code for list manipulation import java.util.List; import java.util.ArrayList;
© ASERT 2006-2016
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); } } }
Groovy code for list manipulation import java.util.List; import java.util.ArrayList;
© ASERT 2006-2016
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
Some Java Boilerplate identified import java.util.List; import java.util.ArrayList;
© ASERT 2006-2016
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?
Java Boilerplate removed
© ASERT 2006-2016
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) }
More Java Boilerplate identified
© ASERT 2006-2016
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?
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 } © ASERT 2006-2016
Output:
["Ted", "Fred", "Jed", "Ned"] 3 Ted Jed Ned
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
© ASERT 2006-2016
// plus a DSL implementation
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
© ASERT 2006-2016
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 }
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
© ASERT 2006-2016
• Or use GDSL (IntelliJ IDEA) or DSLD (Eclipse)
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
© ASERT 2006-2016
… 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
Or typed Groovy DSL version if required
© ASERT 2006-2016
Groovy DSL being debugged
© ASERT 2006-2016
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 } © ASERT 2006-2016
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 @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 } © ASERT 2006-2016
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 @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 } © ASERT 2006-2016
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) } } } }
What style of language is Groovy?
Groovy Style • Imperative/OO but somewhat paradigm agnostic • Dynamic, optionally static (gradual extensible typing) • Extensible language features through metaprogramming
What makes Groovy Awesome? • • • • • •
Java integration Scripting support Multiparadigm Gradual typing Metaprogramming Domain specific language (DSL) support • Ecosystem • Community/Team
Awesome
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
Java Integration • Groovy is Java’s friend
Awesome
Groovy Java
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
Groovy Java
Java Integration • Seamless integration – IDEs provide crosslanguage compile, navigation, and refactoring – Arbitrarily mix source language – Drop-in replace any class – Overloaded methods – Syntax alignment – Shared data types
Groovy Java
Java Integration • Seamless integration – IDEs provide crosslanguage compile, navigation, and refactoring – Arbitrarily mix source language – Drop-in replace any class – Overloaded methods – Syntax alignment – Shared data types
Groovy Java
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)
Java Integration • Polyglot friendly
JavaScript
– JSR-223 scripting to talk to JavaScript
Java
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
Groovy
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 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
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()
Static compilation speed Fibonacci micro-benchmark 30
25
20
15
10
5
0
What makes Groovy Awesome? • • • •
Java integration Scripting support Multiparadigm Gradual typing
• Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team
Runtime metaprogramming
– Add instance & static methods, constructors, properties at runtime – Intercept method/property access – Catch missing methods, properties – Used for dynamic builders, aspectoriented programming, test stubs, mocks & dummies
Awesome*
* Individual levels of awesomeness may vary
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'
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])
XML Parsing/GPath expressions
def xml = ''' ''' def hosts = new XmlParser().parseText(xml) assert hosts.host.service[0].@name=='MyMicroService'
XML Parsing/GPath expressions
def xml = ''' ''' def hosts = new XmlParser().parseText(xml) assert hosts.host.service[0].@name=='MyMicroService'
Compile-time metaprogramming
– Gives you the ability to change the language by augmenting the compilation process
Awesome
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)
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)
Parsing Summary MyScript.groovy
println "Howdy Y'all" > groovy MyScript.groovy > groovyc MyScript.groovy > groovysh > groovyConsole BlockStatement -> ReturnStatement -> MethodCallExpression -> VariableExpression("this") -> ConstantExpression("println") -> ArgumentListExpression -> ConstantExpression("Howdy Y'all")
public run() ... L1 ALOAD 1 LDC 1 AALOAD ALOAD 0 LDC "Howdy Y'all" INVOKEINTERFACE callCurrent() ARETURN ...
Parsing Summary Initialization Parsing Conversion Semantic Analysis
Canonicalization Instruction Selection Class Generation Output Finalization
• 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
Parsing - Early Stages Initialization Parsing Conversion Semantic Analysis
Canonicalization
@ToString class Greeter { String message = "Howdy Y'all" void greet() { println message } } ClassNode: Greeter MethodNode: greet
Instruction Selection
methods:
MethodCall: this.println(message)
Class Generation properties:
Output Finalization
BlockStatement
Property: message type: unresolved(String)
annotations: AnnotationNode: ToString
type: unresolved(ToString)
Parsing - Middle Stages Initialization Parsing Conversion Semantic Analysis
ClassNode: Greeter methods:
Canonicalization
MethodNode: greet MethodNode: getMessage MethodNode: setMessage
Instruction Selection
MethodNode: toString Class Generation Output Finalization
MethodNode: getMetaClass … fields:
FieldNode: message type: resolved(String)
constructors:
ConstructorNode
03
Parsing - Final Stages Initialization Parsing Conversion Semantic Analysis
Canonicalization Instruction Selection Class Generation Output Finalization
public greet()V ... L1 ... ALOAD 0 GETFIELD Greeter.message INVOKEINTERFACE callCurrent() POP ...
Immutable Classes • Some Rules – Don’t provide mutators – Ensure that no methods can be overridden • Easiest to make the class final • Or use static factories & non-public constructors
– Make all fields final – Make all fields private • Avoid even public immutable constants
– Ensure exclusive access to any mutable components • Don’t leak internal references • Defensive copying in and out
– Optionally provide equals and hashCode methods – Optionally provide toString method
@Immutable... • Java Immutable Class – As per Joshua Bloch Effective Java
// ... @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; }
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 String toString() { return "Person(first:" + first + ", last:" + last + ")"; } }
...@Immutable... • Java Immutable Class
boilerplate
– As per Joshua Bloch Effective Java
// ... @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; }
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 String toString() { return "Person(first:" + first + ", last:" + last + ")"; } }
...@Immutable @Immutable class Person { String first, last }
Awesome
With Macros (coming in 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 { new Date() }
Awesome
Macro examples (coming in 2.5) • nullSafe
Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser.
class X { String name } class Y { List list } class Z { Y y }
def getName(Z z) { z.y.list[0].name }
Macro examples (coming in 2.5) • nullSafe
Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser.
class X { String name } class Y { List list } class Z { Y y }
def getName(Z z) { z.y.list[0].name }
Macro examples (coming in 2.5) • nullSafe
Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser.
class X { String name } class Y { List list } class Z { Y y }
def getName(Z z) { z.y.list[0].name }
Prone to NPE
Macro examples (coming in 2.5) • nullSafe
Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser.
class X { String name } class Y {def getName(Z z) { def result = null List list if (z != null && z.y != null && } z.y.list != null && z.y.list[0] != null) { result = z.y.list[0].name class Z { } Y y result } }
Macro examples (coming in 2.5) • nullSafe
Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser.
class X { String name } class Y {def getName(Z z) { def result = null List list if (z != null && z.y != null && } z.y.list != null && z.y.list[0] != null) { result = z.y.list[0].name class Z { } Y y result } }
Verbose
Macro examples (coming in 2.5) Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser.
class X { String name } class Y { List list } class Z { Y y }
def getName(Z z) { z?.y?.list?[0]?.name }
Macro examples (coming in 2.5) Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser.
class X { String name } class Y { List list } class Z { Y y }
def getName(Z z) { nullSafe(z.y.list[0].name) }
Adds in the’?’ automatically
Macro examples (coming in 2.5) See: https://github.com/touchez-du-bois/akatsuki
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
Macro examples (coming in 2.5) • 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 | 1 | 3 | 7 | 4 | 0 | 0 | } }
c 3 7 0
Macro examples (coming in 2.5) See: https://github.com/touchez-du-bois/akatsuki
doWithData { dowith: assert a where: a | b || 1 | 2 || 4 | 5 || 7 | 8 || }
+ b == c c 3 9 15
What makes Groovy Awesome? • • • • •
Java integration Scripting support Multiparadigm Gradual typing Metaprogramming
• Domain Specific Language support – Command chains – Various examples • Ecosystem • Community/Team
Command Chains • Ability to chain method calls without parentheses and dots
Awesome
Command Chains • Ability to chain method calls without parentheses and dots move forward
at 3.km/h
Command Chains • Ability to chain method calls without parentheses and dots move forward
at 3.km/h
• Equivalent to: move(forward).at(3.getKm().div(h))
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
Command chains in DSLs show = { println it } square_root = { Math.sqrt(it) } def please(action) { [the: { what -> [of: { n -> action(what(n)) }] }] } please show // ==> 10.0
the square_root
of 100
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
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 …
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
Logic programming example
cranes have 2 legs tortoises have 4 legs there are 7 animals there are 20 legs
How many of each animal?
Recall: Constraint programming example ChocoCraneTortoise.groovy @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 "$numCranes\n$numTortoises" else println "No Solutions" Cranes = 4
Tortoises = 3
Logic programming example dslUntyped/ChocoCraneTortoiseDSL.groovy 50 lines to define the DSL
Logic programming example dslUntyped/ChocoCraneTortoiseDSL.groovy 50 lines to define the DSL
cranes have 2 legs tortoises have 4 legs
there are 7 animals there are 20 legs display solution
Cranes 4 Tortoises 3
Logic programming example dslUntyped/ChocoCraneTortoiseDSL.groovy 50 lines to define the DSL
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
Logic programming example 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 }
Solution: Tortoise = 3, Beetle = 23 Solution: Crane = 1, Tortoise = 1, Beetle = 24 Solution: Crane = 25, Centipede = 1
Logic programming example 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 }
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)) } }
DSL Type Provider Custom checker
provider/ChocoCraneTortoiseProvider.groovy 80 lines to define the DSL 25 lines to define the provider
@TypeChecked(extensions='NatureServeAnimalProvider.groovy') def main() { animals seen include SandhillCrane, GopherTortoise, ChihuahuanMillipede leg count is 1020 head count is 8 display solution }
Ecosystem Awesome
Awesome
Awesome
Awesome
© ASERT 2006-2016
Groovy's Awesome Team
© ASERT 2006-2016
Groovy's Awesome Team
© ASERT 2006-2016
Direct downloads: 2013: approx. 3 million 2014: 4+ million 2015: 7+ million in first 3 qtrs
Groovy's Awesome Team +100's of others
© ASERT 2006-2016
Groovy's Awesome Team +100's of others just like YOU!
© ASERT 2006-2016
More Information: Groovy in Action Awesome
© ASERT 2006-2016
Bonus Material
45
Audience Puzzle • There are 6 heads • There are 14 legs
46
Audience Puzzle • There are 6 heads • There are 14 legs
47
Other solution • There are 6 heads • There are 14 legs
48
Other solution • There are 6 heads • There are 14 legs
Functional style class RecursiveCalc { int accumulate(int n) { n