! • 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 » 16
stringExtensions moduleVersion = 1.0 // comma separated list of FQN class names extensionClasses = foo.StringExtension // comma separated list of FQN class names staticExtensionClasses = foo.StaticStringExtension 21
• A new binary representation: int x = 0b10101111 assert x == 175 byte aByte = 0b00100001 assert aByte == 33 int anInt = 0b1010000101000101 assert anInt == 41285 23
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 28
– 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 32
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' 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' Compilation error 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' Compilation error Compilation error 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' Compilation error Compilation error Compilation error 36
= " 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 } } 38
= " 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 38
= " 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 38
= " 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 38
= " 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 38
{ // 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() } 39
if (val instanceof String) { println val.toUpperCase() } else if (val instanceof Number) { println "X" * val.intValue() } } No need for casts No need for casts 40
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) 40
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 ! } 42
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 OK – if enough type information is available 44
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 46
•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 47
features •metaclass changes, categories – Method « dynamic dispatch » can differ •but thanks to type inference, it’s as close as «classical» Groovy as possible 49
{ // 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() } 50
{ // 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 50
{ // 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! 50
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 52
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' } 59
strategy • Excellent for... – documenting your APIs – the integration within the IDE •code completion, code navigation – works well with static type checking and static compilation 62
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... } 64
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 64
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 70
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... 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... Java 8 lambdas can be more concise than Groovy! 78
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! 78
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! 78
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! 78
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! Go beyond Java, by making it work on abstract classes too 78
delegate method calls and property accesses to a delegatee class Person { String name void sayHi() { println "Hi $name" } } Handy for DSLs! name = "Guillaume" sayHi() 79
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 79
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() 79
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 80
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 80
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 80
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 80
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! 80
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) 83
{ "I believe I can fly!" } } A trait keyword applying the @Trait transformation class Car implements FlyingAbility {} A class «implements» the trait 87
{ "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!" 87