! • 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 »
= stringExtensions moduleVersion = 1.0 // comma separated list of FQN class names extensionClasses = foo.StringExtension // comma separated list of FQN class names staticExtensionClasses = foo.StaticStringExtension
• A new binary representation: int x = 0b10101111 assert x == 175 byte aByte = 0b00100001 assert aByte == 33 int anInt = 0b1010000101000101 assert anInt == 41285
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
• 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
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'
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
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
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
= " 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 } }
= " 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
= " 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
= " 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
= " 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
{ // 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() }
{ // 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 checked
if (val instanceof String) { println val.toUpperCase() } else if (val instanceof Number) { println "X" * val.intValue() } } No need for casts No need for casts
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#multuply(int)
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 ! }
at 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
lots of type information was infered... so we can as well compile staticallyment ! • ie. generate the same bytecode as javac • Also interesting when stuck on JDK < 7 to gain performance improvements
• 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
features • metaclass changes, categories • Method « dynamic dispatch » can differ • but thanks to type inference, it’s as close as «classical» Groovy as possible
{ // 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() }
{ // 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
{ // 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
{ // 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!
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
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 »
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' }
strategy • Excellent for... • documenting your APIs • the integration within the IDE • code completion, code navigation • works well with static type checking and static compilation
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... }
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
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
delegate method calls and property accesses to a delegatee class Person { String name void sayHi() { println "Hi $name" } } Handy for DSLs! name = "Guillaume" sayHi()
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
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()
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"
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
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
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
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
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!
• including lazy computation @Cached createService() { ... } However, result is cached only on first invocation, but could be expanded to work more like Closure’s memoize() to cache return values depending on different input parameter values
{} 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
{} 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
test(T t) } List<T> filter(Predicate<T> p) list.filter((p) -‐> p.age > 18) Given a predicate & a List method to filter according to that predicate... Java 8 lambdas can be more concise than Groovy!
test(T t) } List<T> filter(Predicate<T> p) list.filter((p) -‐> p.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!
test(T t) } List<T> filter(Predicate<T> p) list.filter((p) -‐> p.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!
test(T t) } List<T> filter(Predicate<T> p) list.filter((p) -‐> p.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!
test(T t) } List<T> filter(Predicate<T> p) list.filter((p) -‐> p.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