Groovy in 2014 and beyond -- Groovy Grails eXchange 2014
As usual, Guillaume Laforge will go through the latest developments of the Groovy programming language and will update you with what's going on, what's to come, and beyond the language itself.
#groovylang @glaforge Traits • Like interfaces, but with method bodies • similar to Java 8 interface default methods • Elegant way to compose behavior • multiple inheritance without the « diamond » problem • Traits can also be stateful • traits can have properties like normal classes • Compatible with static typing and static compilation • class methods from traits also visible from Java classes • Also possible to implement traits at runtime 8
#groovylang @glaforge Traits: a simple example 9 trait
FlyingAbility
{
String
fly()
{
"I'm
flying!"
}
}
class
Bird
implements
FlyingAbility
{}
def
b
=
new
Bird()
assert
b.fly()
==
"I'm
flying!" « trait », a new keyword for a new concept
#groovylang @glaforge Traits: a simple example 9 trait
FlyingAbility
{
String
fly()
{
"I'm
flying!"
}
}
class
Bird
implements
FlyingAbility
{}
def
b
=
new
Bird()
assert
b.fly()
==
"I'm
flying!" a class « implements » a trait
#groovylang @glaforge Traits: a simple example 9 trait
FlyingAbility
{
String
fly()
{
"I'm
flying!"
}
}
class
Bird
implements
FlyingAbility
{}
def
b
=
new
Bird()
assert
b.fly()
==
"I'm
flying!" the fly() method from the trait is available
#groovylang @glaforge Traits: stateful 10 trait
Named
{
String
name
}
class
Bird
implements
Named
{}
def
b
=
new
Bird(name:
'Colibri')
assert
b.name
==
'Colibri'
#groovylang @glaforge Traits: stateful 10 trait
Named
{
String
name
}
class
Bird
implements
Named
{}
def
b
=
new
Bird(name:
'Colibri')
assert
b.name
==
'Colibri' a Groovy property
#groovylang @glaforge Traits: stateful 10 trait
Named
{
String
name
}
class
Bird
implements
Named
{}
def
b
=
new
Bird(name:
'Colibri')
assert
b.name
==
'Colibri' implement the trait
#groovylang @glaforge Traits: stateful 10 trait
Named
{
String
name
}
class
Bird
implements
Named
{}
def
b
=
new
Bird(name:
'Colibri')
assert
b.name
==
'Colibri' Groovy named argument constructor
#groovylang @glaforge Traits: stateful 10 trait
Named
{
String
name
}
class
Bird
implements
Named
{}
def
b
=
new
Bird(name:
'Colibri')
assert
b.name
==
'Colibri' access the property
#groovylang @glaforge Traits: stateful 10 trait
Named
{
String
name
}
class
Bird
implements
Named
{}
def
b
=
new
Bird(name:
'Colibri')
assert
b.name
==
'Colibri'
#groovylang @glaforge Traits: what about conflicts? 12 trait
KiteSurfer
{
String
surf()
{
'kite'
}
}
trait
WebSurfer
{
String
surf()
{
'web'
}
}
class
Person
{
String
name
}
class
Hipster
extends
Person
implements
KiteSurfer,
WebSurfer
{}
def
h
=
new
Hipster()
assert
h.surf()
==
'web' extending a class and implementing the two traits
#groovylang @glaforge Traits: what about conflicts? 14 trait
KiteSurfer
{
String
surf()
{
'kite'
}
}
trait
WebSurfer
{
String
surf()
{
'web'
}
}
class
Person
{
String
name
}
class
Hipster
extends
Person
implements
WebSurfer,
KiteSurfer
{
String
surf()
{
KiteSurfer.super.surf()
}
}
def
h
=
new
Hipster()
assert
h.surf()
==
'kite' Your class method takes precedence over the traits
#groovylang @glaforge Traits: runtime implementation 15 trait
Named
{
String
name
}
class
Animal
{}
class
NamedAnimal
implements
Named
{}
def
na
=
new
NamedAnimal(name:
'Felix')
assert
na.name
==
'Felix'
#groovylang @glaforge Traits: runtime implementation 15 trait
Named
{
String
name
}
class
Animal
{}
class
NamedAnimal
implements
Named
{}
def
na
=
new
NamedAnimal(name:
'Felix')
assert
na.name
==
'Felix' Somewhat artificial to have to create an intermediary class to get named animals
#groovylang @glaforge Traits: runtime implementation 15 trait
Named
{
String
name
}
class
Animal
{}
class
NamedAnimal
implements
Named
{}
def
na
=
new
NamedAnimal(name:
'Felix')
assert
na.name
==
'Felix'
#groovylang @glaforge Traits: runtime implementation 16 trait
Named
{
String
name
}
class
Animal
{}
def
na
=
new
Animal()
as
Named
na.name
=
'Felix'
assert
na.name
==
'Felix'
#groovylang @glaforge Traits: runtime implementation 16 trait
Named
{
String
name
}
class
Animal
{}
def
na
=
new
Animal()
as
Named
na.name
=
'Felix'
assert
na.name
==
'Felix' Runtime trait, with Groovy’s usual coercion mechanism
#groovylang @glaforge Traits: runtime implementation 16 trait
Named
{
String
name
}
class
Animal
{}
def
na
=
new
Animal()
as
Named
na.name
=
'Felix'
assert
na.name
==
'Felix'
#groovylang @glaforge New: @TailRecursive 19 import
groovy.transform.TailRecursive
@TailRecursive
def
fact(BigInteger
n,
accu
=
1G)
{
if
(n
<
2)
accu
else
fact(n
-‐
1,
n
*
accu)
}
assert
fact(1000)
>
10e2566 Downside of tail recursion is you might have to rewrite your algo to be tailrec friendly
#groovylang @glaforge New: @Sortable 20 import
groovy.transform.*
@Sortable
class
Person
{
String
lastName
String
firstName
int
age
} Makes the class Comparable by multiple Comparators
#groovylang @glaforge New: @Sortable 20 import
groovy.transform.*
@Sortable
class
Person
{
String
lastName
String
firstName
int
age
} First compare by lastName, then by firstName, etc.
#groovylang @glaforge New: @Sortable 20 import
groovy.transform.*
@Sortable
class
Person
{
String
lastName
String
firstName
int
age
} You can also specify ‘includes’ / ‘excludes’ properties
#groovylang @glaforge New: @Builder for fluent APIs 21 import
groovy.transform.builder.Builder
@Builder class
Person
{
String
firstName
String
lastName
int
age } def
person
=
Person.builder()
.firstName("Robert")
.lastName("Lewandowski")
.age(21)
.build()
#groovylang @glaforge New: @Builder for fluent APIs 21 import
groovy.transform.builder.Builder
@Builder class
Person
{
String
firstName
String
lastName
int
age } def
person
=
Person.builder()
.firstName("Robert")
.lastName("Lewandowski")
.age(21)
.build() Different implementation strategies available
#groovylang @glaforge New: @Builder for fluent APIs 21 import
groovy.transform.builder.Builder
@Builder class
Person
{
String
firstName
String
lastName
int
age } def
person
=
Person.builder()
.firstName("Robert")
.lastName("Lewandowski")
.age(21)
.build()
#groovylang @glaforge @BaseScript improvements 22 abstract
class
CustomBase
extends
Script
{
int
meaningOfLife
=
42
} @BaseScript(CustomBase)
import
groovy.transform.BaseScript
assert
meaningOfLife
==
42 You can add your own base methods and properties to all compiled scripts
#groovylang @glaforge @BaseScript improvements 22 abstract
class
CustomBase
extends
Script
{
int
meaningOfLife
=
42
} @BaseScript(CustomBase)
import
groovy.transform.BaseScript
assert
meaningOfLife
==
42 Define the base script class for this script
#groovylang @glaforge JDK 7+ NIO2 module • All the familiar methods on File retrofitted on Path as well 25 path.withReader
{
Reader
r
-‐>
...
}
path.eachLine
{
String
line
-‐>
...
}
path.eachFileRecurse
{
Path
p
-‐>
...
}
path
<<
'some
content'
path
<<
bytes
path.readLines()
… Feature request to add all the java.nio.file.Files static utility methods as GDK
#groovylang @glaforge JSON parser / builder perf. increase • Re-implementation of JSON support for speed & efficiency • parser forked off the Boon JSON project • serializer carefully fine-tuned • Article on the parsing speed improvements • http://rick-hightower.blogspot.fr/2014/04/groovy-and-boon-provide-fastest-json.html 27 Benchmark gives 3x to 4x performance factor over Jackson and GSON
#groovylang @glaforge New modes for parsing • Original JsonSlurper renamed to JsonSlurperClassic • Additional parsing modes: • INDEX_OVERLAY: super fast for <2MB payloads • using a « parsing overlay » technique • CHARACTER_SOURCE: for >2MB payloads • implemented with sliding windows over readers • LAX: beyond the JSON spec, nice for configuration files • support single quotes, / and # comments • CHAR_BUFFER: general purpose 28
#groovylang @glaforge Markup template engine • Based on the principles of Groovy’s « builders » • and particularly the MarkupBuilder class for generating arbitrary XML / HTML payloads • Compiled statically for fast template rendering • Internationalization aware • provide the desired Locale in the configuration object • usual suffix notation template_fr_FR.tpl • Custom base template class • ability to provide reusable methods across your templates 31
#groovylang @glaforge Markup template engine • Based on the principles of Groovy’s « builders » • and particularly the MarkupBuilder class for generating arbitrary XML / HTML payloads • Compiled statically for fast template rendering • Internationalization aware • provide the desired Locale in the configuration object • usual suffix notation template_fr_FR.tpl • Custom base template class • ability to provide reusable methods across your templates 31 Spring Boot approved
#groovylang @glaforge Markup template engine — the idea 32 cars
{
cars.each
{
car(make:
it.make,
name:
it.name)
}
} model = [cars: [ new Car(make: 'Peugeot', name: '508'), new Car(make: 'Toyota', name: 'Prius’) ]] Feed a model into your template
#groovylang @glaforge Markup template engine — static! • Type-checked templates available • use createTypeCheckedModelTemplate() instead of createTemplate() • Advantages • get compilation errors • if a variable is not available • if you make mistakes in the code snippets • even faster templates 35
#groovylang @glaforge Android support • You can use Groovy to code Android apps! • use Groovy 2.4.0-beta-1+ • prefer @CompileStatic • Two great posts to get started: • http://melix.github.io/blog/2014/06/grooid.html • http://melix.github.io/blog/2014/06/grooid2.html 43
#groovylang @glaforge Type constraints on traits with @SelfType • Only apply a trait to some child classes 46 class
Component
{
void
doSomething()
{} }
#groovylang @glaforge StreamingTemplateEngine • Uses writable closures to stream the output • Works with templates bigger than 64K 47 import
groovy.text.*
def
eng
=
new
StreamingTemplateEngine()
def
tpl
=
eng.createTemplate
'''
Hi
${name} '''
println
tpl.make(name:
'Guillaume')
#groovylang @glaforge Groovy Macros • Sergei Egorov wants to contribute a macro module • https://github.com/groovy/groovy-core/pull/470 • Simplify creation of AST transformations • less boilerplate manipulating the Groovy AST API • more powerful and less limited than AstBuilder 49
#groovylang @glaforge Groovy Macros • Authoring AST transformations can be verbose: 50 def
someVariable
=
new
ConstantExpression("xyz")
def
returnStatement
=
new
ReturnStatement(
new
ConstructorCallExpression(
ClassHelper.make(SomeCoolClass),
new
ArgumentListExpression(someVariable)
)
)
#groovylang @glaforge Antlr v4 grammar • Problems • Groovy still uses Antlr v2! • but version 3 and 4 are out • Groovy’s grammar evolved from a Java grammar • harder to fix and evolve, especially with Antlr v2 • Advantages • Start from a clean slate • Antlr 4 more tolerant and powerful regarding ambiguities • Time to clean some grammar & syntax warts! • Need to implement the Java 8 constructs! 55
#groovylang @glaforge Antlr v4 grammar • Problems • Groovy still uses Antlr v2! • but version 3 and 4 are out • Groovy’s grammar evolved from a Java grammar • harder to fix and evolve, especially with Antlr v2 • Advantages • Start from a clean slate • Antlr 4 more tolerant and powerful regarding ambiguities • Time to clean some grammar & syntax warts! • Need to implement the Java 8 constructs! 55 A « Google Summer of Code » student helped kick start it
#groovylang @glaforge Java 8 support • Additional grammar & semantic features to support • to keep saying Groovy / Java interoperability is awesome! • New in Java 8 • lambdas • method references • default methods in interfaces • stream API, date / time API • annotations on types & repeated annotations 57
#groovylang @glaforge Java 8 support • Additional grammar & semantic features to support • to keep saying Groovy / Java interoperability is awesome! • New in Java 8 • lambdas • method references • default methods in interfaces • stream API, date / time API • annotations on types & repeated annotations 57 Groovy had already: closures, method pointers, mixins, enriched collection & time APIs
#groovylang @glaforge Goals for the new MOP • Leverage & build upon JDK 7+ invoke dynamic • get Java-like performance even for dynamic code • Rationalize the sedimentation of meta-programming • more coherence, less corner cases & inconsistencies • Provide a notion of « realm » • shield users of « monkey patching » • finer-grained control of meta-programming reach • Private visibility anyone? 59