Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Apache Groovy keynote at G3 Summit

Apache Groovy keynote at G3 Summit

Reflecting on the Apache Groovy history and future, and covering the new features coming up in Groovy 2.4.x / 2.5 / 3.0

Guillaume Laforge

November 29, 2016
Tweet

More Decks by Guillaume Laforge

Other Decks in Technology

Transcript

  1. The Apache 

    Groovy Keynote!
    Guillaume Laforge

    @glaforge

    PMC Chair of 

    Apache Groovy 


    Developer Advocate 

    Google Cloud Platform

    View Slide

  2. Jay Zimmerman

    View Slide

  3. Ready for some pictures?

    View Slide

  4. The Bee Gees of my life!

    View Slide

  5. The Big G’s of my life!

    View Slide

  6. What is the
    future of
    Apache
    Groovy?

    View Slide

  7. The Groovy philosophy

    View Slide

  8. Java’s cousin

    View Slide

  9. A “niche”
    language?

    View Slide

  10. Should we be 

    more aggressive?

    View Slide

  11. Should
    we claim
    Java’s
    throne?

    View Slide

  12. My precious power feature…

    View Slide

  13. Beware
    feature

    View Slide

  14. Exponential complexity

    View Slide

  15. The Groovy bloom

    View Slide

  16. 2M+ downloads
    a month

    View Slide

  17. In the top 20

    View Slide

  18. 2000+
    followers

    View Slide

  19. 4700+
    members

    View Slide

  20. 400+
    members

    View Slide

  21. Groovy’s adoption

    View Slide

  22. Grails

    View Slide

  23. The Gradle
    elephant in the room

    View Slide

  24. The island…

    View Slide

  25. The untold Roadmap

    View Slide

  26. A new grammar

    View Slide

  27. Be followers?

    View Slide

  28. Be
    different?

    View Slide

  29. Copy’n paste
    compatibility

    View Slide

  30. With Java 8,
    Apache Groovy
    still makes sense?

    View Slide

  31. Dynamicity
    unmatched

    View Slide

  32. Little
    details
    matter

    View Slide

  33. Moving
    the needle
    forward

    View Slide

  34. Developer
    productivity

    View Slide

  35. Are we just 

    playing 

    catch 

    up?

    View Slide

  36. Who’s catching up?

    View Slide

  37. Think
    about
    JSON!

    View Slide

  38. Migrating to
    Java idioms?

    View Slide

  39. Shed
    some
    weight?

    View Slide

  40. Breaking
    changes

    View Slide

  41. Backward…
    compatibility matters

    View Slide

  42. MOP

    View Slide

  43. Jigsaw

    View Slide

  44. The
    dark
    days

    View Slide

  45. Surviving…

    View Slide

  46. A competing landscape

    View Slide

  47. The Eclipse

    View Slide

  48. Paid
    committers?

    View Slide

  49. Two paces…

    View Slide

  50. The old guard

    View Slide

  51. James Strachan

    View Slide

  52. A new hope!

    View Slide

  53. You’re the future
    of Apache Groovy!

    View Slide

  54. Show me the code!
    Groovy 2.4.x, 2.5 and 3.0

    View Slide

  55. 55
    @Canonical becomes a meta-annotation
    import groovy.transform.*


    @Canonical(includeNames = true)

    class Person {

    String name

    int age

    }


    assert new Person('Guillaume', 39).toString() ==

    'Person(name:Guillaume, age:39)'

    View Slide

  56. 55
    @Canonical becomes a meta-annotation
    import groovy.transform.*


    @Canonical(includeNames = true)

    class Person {

    String name

    int age

    }


    assert new Person('Guillaume', 39).toString() ==

    'Person(name:Guillaume, age:39)'
    includeNames
    from @ToString

    View Slide

  57. 56
    More control on annotation collector
    DUPLICATE
    Annotations from the annotation
    collection will always be inserted.
    PREFER_COLLECTOR
    Annotations from the collector will be
    added and any existing annotations
    with the same name will be removed.
    PREFER_EXPLICIT
    Annotations from the collector will be
    ignored if any existing annotations with
    the same name are found.
    PREFER_EXPLICIT_MERGED
    Annotations from the collector will be
    ignored if any existing annotations with
    the same name are found but any new
    parameters on the collector annotation
    will be added to existing annotations.
    PREFER_COLLECTOR_MERGED
    Annotations from the collector will be
    added and any existing annotations
    with the same name will be removed
    but any new parameters found within
    existing annotations will be merged
    into the added annotation.

    View Slide

  58. 57
    Pre/post events for @TupleConstructor
    import groovy.transform.TupleConstructor
    @TupleConstructor(pre = { first = first?.toLowerCase() })
    class Person {
    String first
    }
    def p = new Person('Jack')
    assert p.first == 'jack'

    View Slide

  59. 57
    Pre/post events for @TupleConstructor
    import groovy.transform.TupleConstructor
    @TupleConstructor(pre = { first = first?.toLowerCase() })
    class Person {
    String first
    }
    def p = new Person('Jack')
    assert p.first == 'jack'
    import groovy.transform.TupleConstructor
    import static groovy.test.GroovyAssert.shouldFail
    @TupleConstructor(pre = { assert first })
    class Person {
    String first
    }
    def p = new Person('Jack')
    shouldFail {
    def unknown = new Person()
    }

    View Slide

  60. 58
    Prevent @TupleConstructor default ctors
    @TupleConstructor

    class Person {

    String first, last

    int age

    }

    View Slide

  61. 58
    Prevent @TupleConstructor default ctors
    @TupleConstructor

    class Person {

    String first, last

    int age

    }
    Generates:
    Person(String first, String last, int age) { /*...*/ }
    Person(String first, String last) { this(first, last, 0) }
    Person(String first) { this(first, null) }
    Person() { this(null) }

    View Slide

  62. 59
    Prevent @TupleConstructor default ctors
    @TupleConstructor(defaults = true)

    class Person {
    String first, last

    int age

    }

    View Slide

  63. 59
    Prevent @TupleConstructor default ctors
    @TupleConstructor(defaults = true)

    class Person {
    String first, last

    int age

    }
    Generates only:
    Person(String first, String last, int age) { /*...*/ }

    View Slide

  64. 60
    New @MapConstructor transformation
    import groovy.transform.*


    @TupleConstructor

    class Person {

    String first, last

    }


    @CompileStatic // optional

    @ToString(includeSuperProperties = true)

    @MapConstructor(pre = { super(args?.first, args?.last);

    args = args ?: [:] },

    post = { first = first?.toUpperCase() })

    class Author extends Person {

    String bookName

    }
    assert new Author(first: 'Dierk',
    last: 'Koenig',
    bookName: 'ReGinA').toString() ==
    'Author(ReGinA, DIERK, Koenig)'


    assert new Author().toString() ==
    'Author(null, null, null)'

    View Slide

  65. 60
    New @MapConstructor transformation
    import groovy.transform.*


    @TupleConstructor

    class Person {

    String first, last

    }


    @CompileStatic // optional

    @ToString(includeSuperProperties = true)

    @MapConstructor(pre = { super(args?.first, args?.last);

    args = args ?: [:] },

    post = { first = first?.toUpperCase() })

    class Author extends Person {

    String bookName

    }
    assert new Author(first: 'Dierk',
    last: 'Koenig',
    bookName: 'ReGinA').toString() ==
    'Author(ReGinA, DIERK, Koenig)'


    assert new Author().toString() ==
    'Author(null, null, null)'
    Can decorate map
    ctor with pre / post-
    instructions

    View Slide

  66. 61
    Properties validated in AST xforms
    import groovy.transform.AutoClone


    @AutoClone(excludes = 'sirName')

    class Person {

    String firstName

    String surName

    }


    new Person(firstName: "John",
    surName: "Doe").clone()

    View Slide

  67. 61
    Properties validated in AST xforms
    import groovy.transform.AutoClone


    @AutoClone(excludes = 'sirName')

    class Person {

    String firstName

    String surName

    }


    new Person(firstName: "John",
    surName: "Doe").clone()
    Error during @AutoClone
    processing: 'excludes'
    property 'sirName'
    does not exist.

    View Slide

  68. 62
    @Immutable support in class hierarchy
    import groovy.transform.*


    @EqualsAndHashCode

    class Person {

    String name

    }

    View Slide

  69. 62
    @Immutable support in class hierarchy
    import groovy.transform.*


    @EqualsAndHashCode

    class Person {

    String name

    }
    @Immutable

    @TupleConstructor(includeSuperProperties = true)

    @EqualsAndHashCode(callSuper = true)

    @ToString(includeNames = true, includeSuperProperties = true)

    class Athlete extends Person {

    String sport

    }

    View Slide

  70. 62
    @Immutable support in class hierarchy
    import groovy.transform.*


    @EqualsAndHashCode

    class Person {

    String name

    }
    @Immutable

    @TupleConstructor(includeSuperProperties = true)

    @EqualsAndHashCode(callSuper = true)

    @ToString(includeNames = true, includeSuperProperties = true)

    class Athlete extends Person {

    String sport

    }
    def d1 = new Athlete('Michael Jordan', 'BasketBall')

    def d2 = new Athlete(name: 'Roger Federer', sport: 'Tennis')
    assert d1 != d2

    assert d1.toString() == 

    'Athlete(sport:BasketBall, name:Michael Jordan)'

    assert d2.toString() == 

    'Athlete(sport:Tennis, name:Roger Federer)'

    View Slide

  71. 63
    @Immutable supports Optional
    import groovy.transform.Immutable


    @Immutable

    class Person {

    String name

    Optional address

    }


    def p = new Person('Joe', Optional.of('Home'))


    assert p.toString() == 'Person(Joe, Optional[Home])'

    assert p.address.get() == 'Home'

    View Slide

  72. 63
    @Immutable supports Optional
    import groovy.transform.Immutable


    @Immutable

    class Person {

    String name

    Optional address

    }


    def p = new Person('Joe', Optional.of('Home'))


    assert p.toString() == 'Person(Joe, Optional[Home])'

    assert p.address.get() == 'Home'
    @Immutable

    class Person {

    String name

    Optional birth

    }
    Fails compilation,
    as Date is mutable

    View Slide

  73. 64
    New @AutoImplement transformation
    @AutoImplement

    class MyNames extends AbstractList implements Closeable {}

    View Slide

  74. 64
    New @AutoImplement transformation
    @AutoImplement

    class MyNames extends AbstractList implements Closeable {}
    class MyNames extends AbstractList implements Closeable {

    String get(int param0) {

    return null

    }

    boolean addAll(Collection extends String> param0) {

    return false

    }

    void close() throws Exception {

    }

    int size() {

    return 0

    }

    }

    View Slide

  75. 65
    New @AutoImplement transformation
    @AutoImplement(exception = IOException)

    class MyWriter extends Writer { }

    View Slide

  76. 65
    New @AutoImplement transformation
    @AutoImplement(exception = IOException)

    class MyWriter extends Writer { }
    @AutoImplement(exception = UnsupportedOperationException,

    message = 'Not supported by MyIterator')

    class MyIterator implements Iterator { }

    View Slide

  77. 65
    New @AutoImplement transformation
    @AutoImplement(exception = IOException)

    class MyWriter extends Writer { }
    @AutoImplement(exception = UnsupportedOperationException,

    message = 'Not supported by MyIterator')

    class MyIterator implements Iterator { }
    @AutoImplement(code = { throw new UnsupportedOperationException(

    'Should never be called but was called on ' + new Date()) })

    class EmptyIterator implements Iterator {

    boolean hasNext() { false }

    }

    View Slide

  78. 66
    :grab command in groovysh
    groovy:000> :grab 'com.google.guava:guava:19.0'
    groovy:000> import com.google.common.collect.BiMap
    ===> com.google.common.collect.BiMap

    View Slide

  79. 67
    @Delegate on getters too
    class Person {

    String name
    @Delegate

    String getName() {
    name.reverse()
    }
    }
    def p = new Person(name: 'Erine')
    assert p.toUpperCase() == 'ENIRE'

    View Slide

  80. 68
    JAXB marshalling shortcuts
    import groovy.transform.EqualsAndHashCode
    import javax.xml.bind.JAXBContext
    import javax.xml.bind.annotation.*
    @EqualsAndHashCode
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlRootElement
    class Person {
    String name
    int age
    }

    View Slide

  81. 68
    JAXB marshalling shortcuts
    import groovy.transform.EqualsAndHashCode
    import javax.xml.bind.JAXBContext
    import javax.xml.bind.annotation.*
    @EqualsAndHashCode
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlRootElement
    class Person {
    String name
    int age
    }
    def jaxbContext =
    JAXBContext.newInstance(Person)
    def p = new Person(name: 'Marion', age: 8)
    def xml = jaxbContext.marshal(p)
    assert jaxbContext.unmarshal(xml, Person) == p

    View Slide

  82. 69
    CliBuilder supports annotations
    // specify parameters

    def cli = new CliBuilder(usage: 'groovy Greeter [option]')

    cli.a(longOpt: 'audience', args: 1, 'greeting audience')

    cli.h(longOpt: 'help', 'display usage')


    // parse and process parameters

    def options = cli.parse(args)

    if (options.h) cli.usage()

    else println "Hello ${options.a ? options.a : 'World'}"

    View Slide

  83. 70
    CliBuilder supports annotations
    import groovy.cli.*


    interface GreeterI {

    @Option(shortName='h', description='display usage')
    Boolean help()

    @Option(shortName='a', description='greeting audience')
    String audience()

    @Unparsed
    List remaining()

    }

    View Slide

  84. 70
    CliBuilder supports annotations
    import groovy.cli.*


    interface GreeterI {

    @Option(shortName='h', description='display usage')
    Boolean help()

    @Option(shortName='a', description='greeting audience')
    String audience()

    @Unparsed
    List remaining()

    }
    def cli = new CliBuilder(usage: 'groovy Greeter [option]')

    def argz = '--audience Groovologist'.split()

    def options = cli.parseFromSpec(GreeterI, argz)

    assert options.audience() == 'Groovologist'

    View Slide

  85. 71
    With… tap…
    @Canonical

    class Person {

    String name

    int age

    }

    View Slide

  86. 71
    With… tap…
    assert new Person().with {
    name = 'Guillaume'
    age = 39
    return it
    }.toString() == 'Person(Guillaume, 39)'
    @Canonical

    class Person {

    String name

    int age

    }

    View Slide

  87. 71
    With… tap…
    assert new Person().with {
    name = 'Guillaume'
    age = 39
    return it
    }.toString() == 'Person(Guillaume, 39)'
    assert new Person().tap {
    name = 'Guillaume'
    age = 39
    }.toString() == 'Person(Guillaume, 39)'
    @Canonical

    class Person {

    String name

    int age

    }

    View Slide

  88. View Slide

  89. JSON
    generator

    View Slide

  90. 73
    A customizable JSON serializer
    class Person {

    String name

    String title

    int age

    String password

    Date dob

    URL favoriteUrl

    }
    Person person = new Person(name: 'John', title: null, 

    age: 21, password: 'secret',

    dob: Date.parse('yyyy-MM-dd', '1984-12-15'),

    favoriteUrl: new URL('http://groovy-lang.org/'))


    def generator = new JsonGenerator.Options()

    .excludeNulls()

    .dateFormat('[email protected]')

    .excludeFieldsByName('age', 'password')

    .excludeFieldsByType(URL)

    .build()


    assert generator.toJson(person) == 

    '{"dob":"[email protected]","name":"John"}'

    View Slide

  91. 74
    A customizable JSON serializer
    def generator = new JsonGenerator.Options()

    .addConverter(URL) { URL u, String key ->

    if (key == 'favoriteUrl') {

    u.getHost()

    } else {

    u

    }

    }

    .build()

    View Slide

  92. 74
    A customizable JSON serializer
    def generator = new JsonGenerator.Options()

    .addConverter(URL) { URL u, String key ->

    if (key == 'favoriteUrl') {

    u.getHost()

    } else {

    u

    }

    }

    .build()
    Data type to
    customize

    View Slide

  93. 74
    A customizable JSON serializer
    def generator = new JsonGenerator.Options()

    .addConverter(URL) { URL u, String key ->

    if (key == 'favoriteUrl') {

    u.getHost()

    } else {

    u

    }

    }

    .build()
    Data type to
    customize
    Optional key

    View Slide

  94. View Slide

  95. AST
    macros

    View Slide

  96. 76
    AST macros: the old way
    @Retention(RetentionPolicy.SOURCE)

    @Target([ElementType.TYPE])

    @GroovyASTTransformationClass([
    "metaprogramming.AddMethodASTTransformation"])
    @interface AddMethod { }

    View Slide

  97. 76
    AST macros: the old way
    @Retention(RetentionPolicy.SOURCE)

    @Target([ElementType.TYPE])

    @GroovyASTTransformationClass([
    "metaprogramming.AddMethodASTTransformation"])
    @interface AddMethod { }
    Let’s decorate a class 

    with an additional method

    View Slide

  98. 77
    AST macros: the old way
    @GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)

    class AddMethodASTTransformation extends AbstractASTTransformation {

    @Override

    void visit(ASTNode[] nodes, SourceUnit source) {

    ClassNode classNode = (ClassNode) nodes[1]


    ReturnStatement code =

    new ReturnStatement(

    new ConstantExpression("42"))


    MethodNode methodNode =

    new MethodNode("getMessage",

    ACC_PUBLIC, ClassHelper.make(String),

    [] as Parameter[], [] as ClassNode[],

    code)


    classNode.addMethod(methodNode)

    }

    }

    View Slide

  99. 77
    AST macros: the old way
    @GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)

    class AddMethodASTTransformation extends AbstractASTTransformation {

    @Override

    void visit(ASTNode[] nodes, SourceUnit source) {

    ClassNode classNode = (ClassNode) nodes[1]


    ReturnStatement code =

    new ReturnStatement(

    new ConstantExpression("42"))


    MethodNode methodNode =

    new MethodNode("getMessage",

    ACC_PUBLIC, ClassHelper.make(String),

    [] as Parameter[], [] as ClassNode[],

    code)


    classNode.addMethod(methodNode)

    }

    }
    return 42

    View Slide

  100. 78
    AST macros: the new way
    @GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)

    class AddMethodASTTransformation extends AbstractASTTransformation {

    @Override

    void visit(ASTNode[] nodes, SourceUnit source) {

    ClassNode classNode = (ClassNode) nodes[1]


    ReturnStatement simplestCode = macro { return "42" }



    MethodNode methodNode =

    new MethodNode("getMessage",

    ACC_PUBLIC, ClassHelper.make(String),

    [] as Parameter[], [] as ClassNode[],

    code)


    classNode.addMethod(methodNode)

    }

    }

    View Slide

  101. 78
    AST macros: the new way
    @GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)

    class AddMethodASTTransformation extends AbstractASTTransformation {

    @Override

    void visit(ASTNode[] nodes, SourceUnit source) {

    ClassNode classNode = (ClassNode) nodes[1]


    ReturnStatement simplestCode = macro { return "42" }



    MethodNode methodNode =

    new MethodNode("getMessage",

    ACC_PUBLIC, ClassHelper.make(String),

    [] as Parameter[], [] as ClassNode[],

    code)


    classNode.addMethod(methodNode)

    }

    }
    macro method

    View Slide

  102. 78
    AST macros: the new way
    @GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)

    class AddMethodASTTransformation extends AbstractASTTransformation {

    @Override

    void visit(ASTNode[] nodes, SourceUnit source) {

    ClassNode classNode = (ClassNode) nodes[1]


    ReturnStatement simplestCode = macro { return "42" }



    MethodNode methodNode =

    new MethodNode("getMessage",

    ACC_PUBLIC, ClassHelper.make(String),

    [] as Parameter[], [] as ClassNode[],

    code)


    classNode.addMethod(methodNode)

    }

    }
    macro method
    Different variants:
    macro(Closure),
    macro(boolean, Closure)
    macro(CompilePhase,
    Closure)…

    View Slide

  103. 79
    AST macros: variable substitution
    @Retention(RetentionPolicy.SOURCE)

    @Target([ElementType.FIELD])

    @GroovyASTTransformationClass([
    "metaprogramming.MD5ASTTransformation"])

    @interface MD5 { }

    View Slide

  104. 79
    AST macros: variable substitution
    @Retention(RetentionPolicy.SOURCE)

    @Target([ElementType.FIELD])

    @GroovyASTTransformationClass([
    "metaprogramming.MD5ASTTransformation"])

    @interface MD5 { }
    Adds a get${stringGield}MD5()
    method for each @MD5 String field

    View Slide

  105. 80
    AST macros: variable substitution
    BlockStatement buildMD5MethodCode(FieldNode fieldNode) {

    VariableExpression fieldVar = GeneralUtils.varX(fieldNode.name)


    return macro(CompilePhase.SEMANTIC_ANALYSIS, true) {

    return java.security.MessageDigest

    .getInstance('MD5')

    .digest($v { fieldVar }.getBytes())

    .encodeHex()

    .toString()

    }

    }

    View Slide

  106. 80
    AST macros: variable substitution
    BlockStatement buildMD5MethodCode(FieldNode fieldNode) {

    VariableExpression fieldVar = GeneralUtils.varX(fieldNode.name)


    return macro(CompilePhase.SEMANTIC_ANALYSIS, true) {

    return java.security.MessageDigest

    .getInstance('MD5')

    .digest($v { fieldVar }.getBytes())

    .encodeHex()

    .toString()

    }

    }
    Substitute embedded $v value with 

    a variable from the context

    View Slide

  107. 81
    AST macros: macro class
    @GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)

    class StatisticsASTTransformation extends AbstractASTTransformation {

    void visit(ASTNode[] nodes, SourceUnit source) {

    ClassNode classNode = (ClassNode) nodes[1]

    ClassNode templateClass = buildTemplateClass(classNode)


    templateClass.methods.each { MethodNode node ->

    classNode.addMethod(node)

    }

    }


    ClassNode buildTemplateClass(ClassNode reference) {

    def methodCount = constX(reference.methods.size())

    def fieldCount = constX(reference.fields.size())


    return new MacroClass() {

    class Statistics {

    java.lang.Integer getMethodCount() {

    return $v { methodCount }

    }


    java.lang.Integer getFieldCount() {

    return $v { fieldCount }

    }


    View Slide

  108. 81
    AST macros: macro class
    @GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)

    class StatisticsASTTransformation extends AbstractASTTransformation {

    void visit(ASTNode[] nodes, SourceUnit source) {

    ClassNode classNode = (ClassNode) nodes[1]

    ClassNode templateClass = buildTemplateClass(classNode)


    templateClass.methods.each { MethodNode node ->

    classNode.addMethod(node)

    }

    }


    ClassNode buildTemplateClass(ClassNode reference) {

    def methodCount = constX(reference.methods.size())

    def fieldCount = constX(reference.fields.size())


    return new MacroClass() {

    class Statistics {

    java.lang.Integer getMethodCount() {

    return $v { methodCount }

    }


    java.lang.Integer getFieldCount() {

    return $v { fieldCount }

    }

    MacroClass not restricted to
    expressions & statements:
    you can generate
    methods & classes

    View Slide

  109. 82
    AST matchers
    @Retention(RetentionPolicy.SOURCE)

    @Target([ElementType.METHOD])

    @GroovyASTTransformationClass(["JokingASTTransformation"])

    @interface Joking { }

    View Slide

  110. 82
    AST matchers
    @Retention(RetentionPolicy.SOURCE)

    @Target([ElementType.METHOD])

    @GroovyASTTransformationClass(["JokingASTTransformation"])

    @interface Joking { }
    @GroovyASTTransformation(
    phase = CompilePhase.INSTRUCTION_SELECTION)

    class JokingASTTransformation
    extends AbstractASTTransformation {

    void visit(ASTNode[] nodes, SourceUnit source) {

    MethodNode methodNode = nodes[1]


    methodNode

    .getCode()

    .visit(

    new ConvertOnePlusOneToThree(source))

    }

    }

    View Slide

  111. 82
    AST matchers
    @Retention(RetentionPolicy.SOURCE)

    @Target([ElementType.METHOD])

    @GroovyASTTransformationClass(["JokingASTTransformation"])

    @interface Joking { }
    @GroovyASTTransformation(
    phase = CompilePhase.INSTRUCTION_SELECTION)

    class JokingASTTransformation
    extends AbstractASTTransformation {

    void visit(ASTNode[] nodes, SourceUnit source) {

    MethodNode methodNode = nodes[1]


    methodNode

    .getCode()

    .visit(

    new ConvertOnePlusOneToThree(source))

    }

    }
    class ConvertOnePlusOneToThree
    extends ClassCodeExpressionTransformer {

    SourceUnit sourceUnit


    ConvertOnePlusOneToThree(SourceUnit sourceUnit) {

    this.sourceUnit = sourceUnit

    }


    Expression transform(Expression exp) {

    Expression ref = macro { 1 + 1 }


    if (ASTMatcher.matches(ref, exp)) {

    return macro { 3 }

    }


    return super.transform(exp)

    }

    }

    View Slide

  112. 83
    AST matchers
    void testTestingSumExpression() {

    use(ASTMatcher) {

    TwiceASTTransformation sample = new TwiceASTTransformation()

    Expression referenceNode = macro {

    a + a

    }.withConstraints {

    placeholder 'a'

    }


    assert sample

    .sumExpression

    .matches(referenceNode)

    }

    }

    View Slide

  113. 83
    AST matchers
    void testTestingSumExpression() {

    use(ASTMatcher) {

    TwiceASTTransformation sample = new TwiceASTTransformation()

    Expression referenceNode = macro {

    a + a

    }.withConstraints {

    placeholder 'a'

    }


    assert sample

    .sumExpression

    .matches(referenceNode)

    }

    }
    Placeholders to
    match anything

    View Slide

  114. View Slide

  115. Groovy
    “Parrot”!

    View Slide

  116. 85
    Good ol’ do / while loops
    int i = 0


    do {

    println i++

    } while (i < 10)


    assert i == 10

    View Slide

  117. 86
    Identity comparison is back!
    class Person { String name }


    def p1 = new Person(name: 'Marion')

    def p2 = new Person(name: 'Érine')

    def p2copy = p2 


    assert p2 === p2copy

    assert p1 !== p2

    View Slide

  118. 87
    !in and !instanceof
    if (!(1324 instanceof String)) {}

    if (1234 !instanceof String) {}


    assert !(3 in [])

    assert 3 !in []

    View Slide

  119. 88
    Safe indexing ?[ ]
    List list = null

    assert list?[2] == null
    Map map = null

    assert map?['abc'] == null

    View Slide

  120. 89
    Try with resources
    import java.util.zip.GZIPOutputStream


    def input = new File('./NOTICE')

    def output = new File('/tmp/zipped.zip')


    // Java-style

    try (

    FileInputStream fin = new FileInputStream(input);

    GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(output))

    ) {

    byte[] buffer = new byte[4096]

    int nread = 0

    while ((nread = fin.read(buffer)) != -1) {

    out.write(buffer, 0, nread)

    }

    }

    View Slide

  121. 90
    Try with resources
    import java.util.zip.GZIPOutputStream


    def input = new File('./NOTICE')

    def output = new File('/tmp/zipped.zip')


    // Groovy-style :-)

    try (

    fin = new FileInputStream(input)

    out = new GZIPOutputStream(new FileOutputStream(output))

    ) {
    out << fin
    }

    View Slide

  122. 91
    Try with resources
    import java.util.zip.GZIPOutputStream


    def input = new File('./NOTICE')

    def output = new File('/tmp/zipped.zip')


    // Groovy-style :-)




    out << fin

    View Slide

  123. 92
    Lambdas!
    import static java.util.stream.Collectors.toList


    (1..10).forEach((it) -> { println it })


    assert (1..10).stream()

    .filter((it) -> it % 2 == 0)

    .map((it) -> it * 2)

    .collect(toList()) == [4, 8, 12, 16, 20]

    View Slide

  124. 93
    Lambdas!
    // all the shapes
    (x, y) -> x + y

    (x, y) -> { x + y }

    (int x, int y) -> x + y

    View Slide

  125. 93
    Lambdas!
    // all the shapes
    (x, y) -> x + y

    (x, y) -> { x + y }

    (int x, int y) -> x + y
    implicit
    return

    View Slide

  126. 93
    Lambdas!
    // all the shapes
    (x, y) -> x + y

    (x, y) -> { x + y }

    (int x, int y) -> x + y
    def c = (int x, int y = 0) -> x + y

    assert c(1) == 1
    implicit
    return

    View Slide

  127. 93
    Lambdas!
    // all the shapes
    (x, y) -> x + y

    (x, y) -> { x + y }

    (int x, int y) -> x + y
    def c = (int x, int y = 0) -> x + y

    assert c(1) == 1
    implicit
    return
    default
    argument

    View Slide

  128. 94
    Method references — class refs
    import static java.util.stream.Collectors.toList


    // class::staticMethod

    assert ['1', '2', '3'] ==

    [1, 2, 3].stream()

    .map(Integer::toString)

    .collect(toList())


    // class::instanceMethod

    assert ['A', 'B', 'C'] ==

    ['a', 'b', 'c'].stream()

    .map(String::toUpperCase)

    .collect(toList())

    View Slide

  129. 95
    Method references — instance refs
    class Name {

    String name

    char firstLetter() { name[0] }

    static cloneOf(Name n) { new Name(name: n.name + '2') }

    }

    View Slide

  130. 95
    Method references — instance refs
    class Name {

    String name

    char firstLetter() { name[0] }

    static cloneOf(Name n) { new Name(name: n.name + '2') }

    }
    // instance::instanceMethod

    def fl = new Name(name: 'Guillaume')::firstLetter

    assert fl() == 'G'


    // instance::staticMethod

    def n = new Name(name: 'Marion')

    def clone = n::cloneOf

    assert clone(n).name == 'Marion2'

    View Slide

  131. 96
    Method references — constructor refs
    def r = Random::new

    println r().nextInt(10)

    View Slide

  132. 96
    Method references — constructor refs
    def r = Random::new

    println r().nextInt(10)
    def arr2d = String[][]::new

    arr2d(2, 2) == [[null, null], [null, null]]

    View Slide

  133. 97
    Method references — constructor refs
    import groovy.transform.Canonical


    @Canonical

    class Animal {

    String kind

    }

    View Slide

  134. 97
    Method references — constructor refs
    import groovy.transform.Canonical


    @Canonical

    class Animal {

    String kind

    }
    def a = Animal::new

    assert a('lion').kind == 'lion'


    def c = Animal

    assert c::new('cat').kind == 'cat'

    View Slide

  135. 98
    Elvis assignment — Not yet there!
    import groovy.transform.ToString


    @ToString

    class Person {

    String name

    int age

    }
    def p = new Person(name: 'Érine')


    p.with {

    name = name ?: 'unknown'

    age = age ?: 3

    }


    assert p.toString() == 'Person(Érine, 3)'

    View Slide

  136. 98
    Elvis assignment — Not yet there!
    import groovy.transform.ToString


    @ToString

    class Person {

    String name

    int age

    }
    def p = new Person(name: 'Érine')


    p.with {

    name = name ?: 'unknown'

    age = age ?: 3

    }


    assert p.toString() == 'Person(Érine, 3)'
    p.with {

    name ?= 'unknown'

    age ?= 3

    }

    View Slide

  137. 99
    JavaDoc comments saved
    import org.codehaus.groovy.control.*

    import static org.apache.groovy.parser.antlr4.AstBuilder.DOC_COMMENT


    def code = '''

    /**

    * class Foo

    * @author glaforge

    */

    class Foo {

    /** a method */

    void m(int i) {}

    }

    '''

    View Slide

  138. 99
    JavaDoc comments saved
    import org.codehaus.groovy.control.*

    import static org.apache.groovy.parser.antlr4.AstBuilder.DOC_COMMENT


    def code = '''

    /**

    * class Foo

    * @author glaforge

    */

    class Foo {

    /** a method */

    void m(int i) {}

    }

    '''
    def ast = new CompilationUnit().tap {

    addSource 'methodM.groovy', code

    compile Phases.SEMANTIC_ANALYSIS

    }.ast


    def cls = ast.classes[0]

    assert cls.nodeMetaData[DOC_COMMENT]
    .contains('class Foo')

    assert cls.methods[0].nodeMetaData[DOC_COMMENT]
    .contains('a method')

    View Slide

  139. Thanks for your attention!
    Enjoy G3 Summit!

    View Slide

  140. 101
    Picture credits
    The elephant in the room
    http://www.elephantsinthelivingroom.org/backgrounds/elephant-in-room.jpg
    Road
    https://pixabay.com/fr/route-marquage-de-route-rue-miles-166543/
    Prague
    https://pixabay.com/fr/prague-r%C3%A9publique-tch%C3%A8que-ville-1168302/
    Wise men
    http://img03.deviantart.net/4431/i/2012/044/f/6/three_wise_men_color_by_csoro-d4pmlv2.jpg
    Rocket lift-off
    https://pixabay.com/fr/fus%C3%A9e-ses-9-lancement-cap-canaveral-1245696/
    Java beans
    https://pixabay.com/fr/grains-de-caf%C3%A9-caf%C3%A9-caf%C3%A9ine-boire-167001/
    Ring
    http://www.zastavki.com/pictures/originals/2013/Movies__037523_.jpg
    Letter G
    https://pixabay.com/fr/typographie-geschtaltung-fontes-1069409/
    Grammar
    https://pixabay.com/fr/grammaire-abc-dictionnaire-mots-390029/

    View Slide

  141. 102
    Picture credits
    Copy and paste
    https://d262ilb51hltx0.cloudfront.net/max/2000/1*sglxoCq-p9gbefsUfGaNlg.jpeg
    A new hope
    http://static.dolimg.com/lucas/movies/starwars/starwars_epi4_01-4f17cf46f1dd.jpg
    Snails
    https://pixabay.com/fr/escargots-course-gast%C3%A9ropodes-1753611/
    The revenant
    https://static.independent.co.uk/s3fs-public/thumbnails/image/2016/02/18/16/The-Revenant-Leo.jpg
    Broken window
    https://pixabay.com/fr/endroits-perdus-rompu-fen%C3%AAtre-verre-1798639/
    Ducklings
    https://pixabay.com/fr/canard-canetons-les-b%C3%A9b%C3%A9s-caneton-207344/
    Jigsaw
    https://pixabay.com/fr/pi%C3%A8ces-du-puzzle-mix-les-mains-592798/
    Bee Gees
    http://cdn-mag.itcher.com/mag/wp-content/uploads/2015/06/BeeGees.jpg
    Buddha
    https://pixabay.com/fr/bouddha-le-bouddhisme-statue-525883/

    View Slide

  142. 103
    Picture credits
    Cousin
    https://pixabay.com/fr/cousins-jouer-l-ext%C3%A9rieur-jouets-1587250/
    Dollar
    https://pixabay.com/fr/dollar-billet-de-banque-%C3%A9tats-unis-1161782/
    Flower bloom
    https://pixabay.com/fr/fleur-de-printemps-arbre-nature-289844/
    Baby shoes
    https://pixabay.com/fr/chaussures-grossesse-enfant-505471/
    Grails
    https://isimbolinellacomunicazione.files.wordpress.com/2013/02/calice_du_sacre_tau.jpg
    Eclipse
    https://pixabay.com/fr/%C3%A9clipses-solaire-soleil-434338/
    Baseball catch up
    https://pixabay.com/fr/baseball-lecteur-jeu-jouer-1504968/
    Dogs
    https://pixabay.com/fr/chiens-matraques-jouer-morsure-708354/
    Niche / kennel
    https://pixabay.com/fr/chien-niche-cage-peluche-1199846/

    View Slide

  143. 104
    Picture credits
    Fist punch
    https://pixabay.com/fr/poing-coup-puissance-wrestling-1561157/
    Throne
    http://static.srcdn.com/wp-content/uploads/GoT-Players-Iron-Throne-Feature.jpg
    Migrating birds
    https://pixabay.com/fr/troupeau-flocage-oies-migrer-vol-378676/
    Trash
    https://pixabay.com/fr/poubelle-garbage-seau-vert-1111448/
    MOP
    http://i.huffpost.com/gen/1176457/images/o-CLEAN-MOP-facebook.jpg
    Backward snow gliding
    https://pixabay.com/fr/enfant-fille-bob-rouler-sur-1214231/
    Rabbit & tortoise
    https://media.senscritique.com/media/000008120110/1200/Le_Lievre_et_la_Tortue.jpg
    Car lights
    https://pixabay.com/fr/autoroute-photo-de-nuit-feux-nuit-393492/
    Agility
    http://wallpoper.com/images/00/15/50/09/lifestyle-female-dynamic-movement-of-women_00155009.jpg

    View Slide

  144. 105
    Picture credits
    Watch complication
    https://gildedholdings.files.wordpress.com/2014/10/lange-grand-complication-uhrwerk-movement-1-neu.jpg
    Speed meter
    https://pixabay.com/fr/odom%C3%A8tre-1810107/
    Swiss army knife
    https://pixabay.com/fr/arm%C3%A9e-de-terre-lame-compact-coup%C3%A9-15563/
    Punk
    https://pixabay.com/fr/jeune-jeune-fille-jeunesse-punk-175504/
    Jason
    http://static.comicvine.com/uploads/original/10/103184/3150852-2994250-0447705879-jason.jpg
    Creep
    http://orig11.deviantart.net/f87a/f/2012/092/1/5/creep_by_holavengoaflotar-d4urypg.jpg
    Jammed wires
    https://www.exchangewire.com/wp-content/uploads/2016/09/7670055210_ceb0c9ef9a_b.jpg

    View Slide