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
4. 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.”
13. Why Groovy?
Groovy
Make it
simplerMake it
dynamic
Support
simple
scripting
Support
functional
style
Support
concurrency
Good IDE
support
Custom
features
Java
integration
14.
15. 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);
}
}
}
16. 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
17. 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?
18. 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)
}
19. 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?
21. 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
22. 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 }
23. 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
24. 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
27. 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
}
28. 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)
}
}
}
}
29. @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)
}
}
}
}
30. @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
31. @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
35. What makes Groovy Awesome?
• Java integration
• Scripting support
• Multiparadigm
• Gradual typing
• Metaprogramming
• Domain specific
language (DSL) support
• Ecosystem
• Community/Team
Awesome
36. 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
38. 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
39. 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
40. 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
41. 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)
42. 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
43. 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
45. 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
46. 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
58. 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
59. 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
60. 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
65. 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'
66. 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'
67. 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'
68. 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
69. Logic programming example
cranes have 2 legs
tortoises have 4 legs
there are 7 animals
there are 20 legs
How many of each animal?
71. 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
72. 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
73. 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
74. 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
75. 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
76. 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
77. 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
78. 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
79. 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
80. Gradual typing
• Dynamic by default
• Gradual typing
• Static type checking
• Extensible type system
Awesome
81. Dynamic vs static
82
def myPets = ['Lassie', 'Skip']
List<String> yourPets = ['Lassie', 'Skip']
shouldFail(ClassCastException) {
List ourPets = new Date()
}
82. Dynamic vs static
83
Gradual
Type-time?
def myPets = ['Lassie', 'Skip']
List<String> yourPets = ['Lassie', 'Skip']
shouldFail(ClassCastException) {
List ourPets = new Date()
}
83. 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'
84. 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
85. 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()
}
'''
}
86. 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
87. 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.
91. 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
92. What makes Groovy Awesome?
• Java integration
• Scripting support
• Multiparadigm
• Gradual typing
• Metaprogramming
• Runtime
• Compile-time
• Macros
• Domain Specific Language support
• Ecosystem
• Community/Team
93. 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
94. 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'
99. 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)
100. 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)
102. • 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
105. 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
...
106. 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
107. @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 + ")";
}
}
108. ...@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
114. 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
115. 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
116. 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
117. 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.
118. 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
119. 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
120. 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)
}
122. 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
123. 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
}
}
124. 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
}
125. 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
126. 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
128. Command Chains
• Ability to chain method calls without parentheses and dots
move forward at 3.km/h
129. 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))
130. 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
131. 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
132. 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
133. 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 …
134. 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
135. 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
136. Logic programming example
cranes have 2 legs
tortoises have 4 legs
there are 7 animals
there are 20 legs
How many of each animal?
139. 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
140. 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
141. 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
}
142. @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
143. 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))
}
}
144. 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