! • In addition to the language, we have APIs: – template engine, Ant task scripting, Swing UI builder, JMX builder... • We want a lighter « core » – with APIs in the form of modules • Ability to wire in « extension methods » 20
= stringExtensions moduleVersion = 1.0 // comma separated list of FQN class names extensionClasses = foo.StringExtension // comma separated list of FQN class names staticExtensionClasses = foo.StaticStringExtension 25
• A new binary representation: int x = 0b10101111 assert x == 175 byte aByte = 0b00100001 assert aByte == 33 int anInt = 0b1010000101000101 assert anInt == 41285 27
to compile with « indy » – we might propose a backport for JDK < 7 • Avantages – more runtime performance •well... in theory... – In the long term, we might replace •« call site caching » ➔ MethodHandles •« metaclass registry » ➔ ClassValues – and the JIT « inlines » code more easily 32
– say when there’s a typo in a method or variable name – complain when a non-existent method is called – or on bad assignments or use a bad return type 36
variable... int x = new Object() Set set = new Object() String[] strings = ['a','b','c'] int str = strings[0] // cannot find matching method plus() int i = 0 i += '1' 40
variable... int x = new Object() Set set = new Object() String[] strings = ['a','b','c'] int str = strings[0] // cannot find matching method plus() int i = 0 i += '1' Compilation error 40
variable... int x = new Object() Set set = new Object() String[] strings = ['a','b','c'] int str = strings[0] // cannot find matching method plus() int i = 0 i += '1' Compilation error Compilation error 40
variable... int x = new Object() Set set = new Object() String[] strings = ['a','b','c'] int str = strings[0] // cannot find matching method plus() int i = 0 i += '1' Compilation error Compilation error Compilation error 40
= " Guillaume " // String type infered (even inside GString) println "NAME = ${name.toUpperCase()}" // Groovy GDK method support // (GDK operator overloading too) println name.trim() int[] numbers = [1, 2, 3] // Element n is an int for (int n in numbers) { println n } } 42
= " Guillaume " // String type infered (even inside GString) println "NAME = ${name.toUpperCase()}" // Groovy GDK method support // (GDK operator overloading too) println name.trim() int[] numbers = [1, 2, 3] // Element n is an int for (int n in numbers) { println n } } Variable optionally typed 42
= " Guillaume " // String type infered (even inside GString) println "NAME = ${name.toUpperCase()}" // Groovy GDK method support // (GDK operator overloading too) println name.trim() int[] numbers = [1, 2, 3] // Element n is an int for (int n in numbers) { println n } } Variable optionally typed Type String infered 42
= " Guillaume " // String type infered (even inside GString) println "NAME = ${name.toUpperCase()}" // Groovy GDK method support // (GDK operator overloading too) println name.trim() int[] numbers = [1, 2, 3] // Element n is an int for (int n in numbers) { println n } } Variable optionally typed trim() method added dynamically by Groovy Type String infered 42
= " Guillaume " // String type infered (even inside GString) println "NAME = ${name.toUpperCase()}" // Groovy GDK method support // (GDK operator overloading too) println name.trim() int[] numbers = [1, 2, 3] // Element n is an int for (int n in numbers) { println n } } Variable optionally typed Array element type inferred trim() method added dynamically by Groovy Type String infered 42
{ // call method with dynamic behavior // but with proper signature generateMarkup(name.toUpperCase()) } // usual dynamic behavior String generateMarkup(String name) { def sw = new StringWriter() new MarkupBuilder(sw).html { body { div name } } sw.toString() } 43
if (val instanceof String) { println val.toUpperCase() } else if (val instanceof Number) { println "X" * val.intValue() } } No need for casts No need for casts 44
if (val instanceof String) { println val.toUpperCase() } else if (val instanceof Number) { println "X" * val.intValue() } } No need for casts No need for casts Understand GDK’s method: String#multiply(int) 44
type of values assigned into variables @TypeChecked test() { def var = 123 // int infered int x = var // var is an int var = "123" // assign a String into var x = var.toInteger() // no cast needed var = 123 x = var.toUpperCase() // error, var is an int ! } 46
compile-time – @TypeChecked doesn’t change behavior! • do not confound with static compilation • Most dynamic features can’t be checked – metaclass changes, categories... – dynamic variables from the « script binding » • But compile-time metaprogramming is OK – if enough type information is available 48
lots of type information was infered... so we can as well compile statically ! – ie. generate the same bytecode as javac • Also interesting when stuck on JDK < 7 to gain performance improvements 52
•thanks to static type checking –the compiler builds upon it – better performance •close to Java’s performance – code immune to « monkey patching » •dynamic metaprogramming can interfere with your framework’s code – smaller generated bytecode 53
features • metaclass changes, categories – Method « dynamic dispatch » can differ • but thanks to type inference, it’s as close as «classical» Groovy as possible 55
{ // call method with dynamic behavior // but with proper signature generateMarkup(name.toUpperCase()) } // usual dynamic behavior String generateMarkup(String name) { def sw = new StringWriter() new MarkupBuilder(sw).html { body { div name } } sw.toString() } 56
{ // call method with dynamic behavior // but with proper signature generateMarkup(name.toUpperCase()) } // usual dynamic behavior String generateMarkup(String name) { def sw = new StringWriter() new MarkupBuilder(sw).html { body { div name } } sw.toString() } Statically compiled Dynamic Call a method with dynamic content 56
{ // call method with dynamic behavior // but with proper signature generateMarkup(name.toUpperCase()) } // usual dynamic behavior String generateMarkup(String name) { def sw = new StringWriter() new MarkupBuilder(sw).html { body { div name } } sw.toString() } Statically compiled Dynamic Call a method with dynamic content Method signatures are a contract! 56
Static compilation Primitive optimizations No prim. optimizations 191 ms 97 ms 3.6 s 197 ms 101 ms 4.3 s 360 ms 111 ms 23.7 s 2590 ms 3220 ms 50.0 s 1.7 1.8 2.x 58
all method call weren’t going through the « indy » support – only normal method calls – still joint usage of the « call site caching » technique •On JDK 7, with the « indy » JAR, Groovy 2.1 uses « invoke dynamic » everywhere • On JDK < 7, still using « call site caching » • Problem: invalid OSGi bundle, fixed in 2.2 61
certain range of DSLs – « command chains », extension methods... • But less for DSLs using closure delegation – often used by DSLs like in Gradle task copyTask(type: Copy) { from 'src/main/webapp' into 'build/explodedWar' } 66
strategy • Excellent for... – documenting your APIs – the integration within the IDE • code completion, code navigation – works well with static type checking and static compilation 70
to make it smarter! – even smarter than Java’s! :-) • By creating your own extension @TypeChecked(extensions = 'MyExtension.groovy') void exec() { // code to be further checked... } 72
to make it smarter! – even smarter than Java’s! :-) • By creating your own extension @TypeChecked(extensions = 'MyExtension.groovy') void exec() { // code to be further checked... } We could use a meta- annotation 72
add imports transparently – apply AST transformations by default – filter / secure scripts • With the « static type checker » and « static compilation », we were asked if we could apply them by default 78
test(T t) } List<T> filter(Predicate<T> p) list.filter((it) -‐> it.age > 18) Given a predicate & a List method to filter according to that predicate... 86
test(T t) } List<T> filter(Predicate<T> p) list.filter((it) -‐> it.age > 18) Given a predicate & a List method to filter according to that predicate... Java 8 lambdas can be more concise than Groovy! 86
test(T t) } List<T> filter(Predicate<T> p) list.filter((it) -‐> it.age > 18) list.filter({ it.age > 18 } as Predicate) Given a predicate & a List method to filter according to that predicate... Java 8 lambdas can be more concise than Groovy! 86
test(T t) } List<T> filter(Predicate<T> p) list.filter((it) -‐> it.age > 18) list.filter { it.age > 18 } Given a predicate & a List method to filter according to that predicate... Java 8 lambdas can be more concise than Groovy! 86
test(T t) } List<T> filter(Predicate<T> p) list.filter((it) -‐> it.age > 18) list.filter { it.age > 18 } Given a predicate & a List method to filter according to that predicate... Java 8 lambdas can be more concise than Groovy! When no ambiguity, make coercion implicit! 86
test(T t) } List<T> filter(Predicate<T> p) list.filter((it) -‐> it.age > 18) list.filter { it.age > 18 } Given a predicate & a List method to filter according to that predicate... Java 8 lambdas can be more concise than Groovy! When no ambiguity, make coercion implicit! And go beyond Java, by making it work on abstract classes as well 86
delegate method calls and property accesses to a delegatee class Person { String name void sayHi() { println "Hi $name" } } Handy for DSLs! name = "Guillaume" sayHi() 87
delegate method calls and property accesses to a delegatee class Person { String name void sayHi() { println "Hi $name" } } Handy for DSLs! name = "Guillaume" sayHi() Use Person’s name property 87
delegate method calls and property accesses to a delegatee class Person { String name void sayHi() { println "Hi $name" } } Handy for DSLs! name = "Guillaume" sayHi() Use Person’s name property Call Person#sayHi() 87
new CompilerConfiguration() cc.scriptBaseClass = DelegatingScript.class.name def sh = new GroovyShell(cc) def script = sh.parse(file) def p = new Person() script.setDelegate(p) script.run() assert p.name == "Guillaume" Specify DelegatingScript base class 88
new CompilerConfiguration() cc.scriptBaseClass = DelegatingScript.class.name def sh = new GroovyShell(cc) def script = sh.parse(file) def p = new Person() script.setDelegate(p) script.run() assert p.name == "Guillaume" Specify DelegatingScript base class Parse the script 88
new CompilerConfiguration() cc.scriptBaseClass = DelegatingScript.class.name def sh = new GroovyShell(cc) def script = sh.parse(file) def p = new Person() script.setDelegate(p) script.run() assert p.name == "Guillaume" Specify DelegatingScript base class Parse the script Define the delegate 88
new CompilerConfiguration() cc.scriptBaseClass = DelegatingScript.class.name def sh = new GroovyShell(cc) def script = sh.parse(file) def p = new Person() script.setDelegate(p) script.run() assert p.name == "Guillaume" Specify DelegatingScript base class Parse the script Define the delegate Run the script 88
new CompilerConfiguration() cc.scriptBaseClass = DelegatingScript.class.name def sh = new GroovyShell(cc) def script = sh.parse(file) def p = new Person() script.setDelegate(p) script.run() assert p.name == "Guillaume" Specify DelegatingScript base class Parse the script Define the delegate Run the script Be Happy! 88
applied to methods @Memoized int expensiveOp(int a, int b) { sleep 1000 return a + b } // one second to return expensiveOp(1, 2) // immediate result returned expensiveOp(1, 2) 91
{} public <T> Object map( @DelegatesTo.Target List<T> target, @DelegatesTo(genericTypeIndex = 0) Closure arg) { arg.delegate = target.join('') arg() } def test() { map(new MyList(['f', 'o', 'o'])) { assert toUpperCase() == 'FOO' } } We want to delegate to the token type T We define the index of the type token 92
{} public <T> Object map( @DelegatesTo.Target List<T> target, @DelegatesTo(genericTypeIndex = 0) Closure arg) { arg.delegate = target.join('') arg() } def test() { map(new MyList(['f', 'o', 'o'])) { assert toUpperCase() == 'FOO' } } We want to delegate to the token type T We define the index of the type token This is String’s toUpperCase() method 92
{ "I believe I can fly!" } } A trait keyword applying the @Trait transformation class Car implements FlyingAbility {} A class «implements» the trait 96
{ "I believe I can fly!" } } A trait keyword applying the @Trait transformation class Car implements FlyingAbility {} A class «implements» the trait def c = new Car() assert c.fly() == "I believe I can fly!" 96