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