The Apache Foundation recently released version 2.5 of Groovy, with new features including: improvements in AST Transformations; new macro support, and other miscellaneous improvements. Groovy 2.5 requires JDK 7 and runs on JDK 9+ with benign warnings that can be ignored.
Despite the more recent focus on other JVM languages such as Kotlin, for example, Groovy is still experiencing tremendous growth. As Dr. Paul King, principal software engineer at OCI and Groovy committer stated in a recent webinar:
The Groovy download numbers still make it the second most popular language on the JVM after Java and the numbers just keep increasing. For the first quarter of this year, there were 90 million downloads which was twice the number of downloads than the first quarter of last year. So you can see, there is still a lot of interest in Groovy.
Groovy has also gained 30 new committers in the past 12 months.
AST Transformations - Annotations
As shown in the diagram below, a number of the existing AST transformations were improved for consistency among the transformations and 11 new transformations were added for version 2.5. An additional transformation has been added for Groovy 3.0, but more could appear before GA release.
AST Transformations - Macros
As shown in the previous section, there are a large number of built-in AST transformations. Developers could, however, create their own custom transformations, but this required knowing Groovy's internal internal representation of the syntactic structures.
The new macro feature in version 2.5 eliminates the need to know the internal representation of the syntactic structures. As defined in the release notes:
Macros let you use Groovy syntax directly rather than using the internal compiler representations when creating compile-time metaprogramming extensions. This puts the creation of transformations in the hands of all Groovy programmers not just Groovy compiler gurus.
For example, say a developer wishes to create a transformation, @Info
, that generates a method, @getInfo()
. Before version 2.5, it was necessary to write the following code:
...
def clazz = new MethodCallExpression(new VariableExpression("this"), "getClass", EMPTY_ARGUMENTS)
def body = new ExpressionStatement(new MethodCallExpression(clazz, "getName", EMPTY_ARGUMENTS))
classNode.addMethod('getInfo', ACC_PUBLIC, STRING_TYPE, EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body)
...
With macros, the first two lines in the code shown above can be replaced with:
def body = macro(true) {
getClass().name
}
More details can be found in the release notes.
Groovy 3.0
Groovy 3.0.0-alpha-3 has been available since late June with beta versions scheduled for later this year and release candidates expected in early 2019.
Groovy 3.0 will require a minimum of JDK 8 with improved support for JDK 9 and above. A new parser, called the Parrot Parser, will be a significant new feature that will support new Groovy syntax.
King spoke to InfoQ on this latest release.
InfoQ: Can you speak to the differences between the default Indy parser and the new Parrot parser for Groovy 3.0?
Paul King: To explain the "Indy" and "Parrot" flavors of the Groovy parser, you need to know a little more detail. Groovy's parser does its work in multiple phases. The early phases take source code and convert it into an internal abstract syntax tree (AST) representation. The later phases convert the AST into bytecode which runs on the JVM in an almost identical fashion to how bytecode produced from the Java compiler runs.
When we speak about the "Parrot" parser, we are talking about Groovy 3's totally revamped early stages of the compiler. It has been re-engineered to be much more flexible and using recent well-supported technologies. This work puts Groovy into a great position to evolve quickly which means we can incorporate changes from Java for "cut-n-paste" compatibility with Java as well as native Groovy changes.
When we speak of the "Indy" support. This is all about the kind of bytecode produced in the later stages of the compiler. In Groovy versions 2.0 through 2.5, we support producing "classic" Groovy bytecode as well as an "indy" version. The indy version makes use of the "INvoke DYnamic" bytecode instruction introduced in JDK 7. The invoke dynamic bytecode instruction was added to the JVM for numerous reasons but a particular use case was to improve the performance for dynamic languages like Groovy. Early versions of the JDK supporting that bytecode instruction were a little buggy and although performance in some areas improved, other areas like our hand-coded primitive optimisation were still faster when using the classic bytecode instructions. For these reasons we have been supporting both variants. The alpha versions of the Groovy 3 compiler still support both versions but there is work underway to make the indy variant the default and potentially incorporate some of the other optimisations so that we can remove the old classic flavor altogether.
InfoQ: With the large number of built-in AST transformations available in Groovy, what would be a typical use case for a developer to write his or her own custom AST transformations?
King: There are numerous use cases for using AST transformations. They remove boilerplate, capture common design patterns and coding styles, and support declarative programming. You are correct that we bundle a lot of useful AST transformations within Groovy but we try to bundle transforms that we think would be widely applicable. I can see some obvious places where developers will still find it useful to create their own:
- If the transformation someone needs is special purpose in nature, we aren't likely going to include that in Groovy.
- If someone doesn't like the behavior our transforms offer and they can't use annotation attributes to customise the behavior, then they have a few options. Firstly, they can use the meta-annotation capability to "bundle" together combinations of our annotations and potentially combine them with for example Spring annotations. If that still doesn't give them what they need, then writing their own is a good option.
- Someone might be writing a DSL specific to their domain and wish to incorporate annotations in the DSL. That is just one option they have when writing DSLs.
- Someone might be writing their own framework and wish to provide powerful annotations to greatly simplify coding for users of the framework.
To give you an example of framework usage of AST transformations, Grails has about 20 AST transformations in grails-core alone. Griffon has added numerous ones also. Micronaut's AST transformation processing handles an aggregate of over 100 annotations.
InfoQ: With the new macro feature, do you anticipate more developers to write their own custom AST transformations?
King: It will no doubt take some time for people to become familiar with macros. But once more examples start to appear, there is often a snowball effect. We'll have to wait and see. I can see us adding numerous ones into the Groovy codebase in any case.
InfoQ: Has the new JDK release cadence affected Groovy development in any way?
King: Yes, there are some really nice aspects to seeing the faster release cadence but it has also been a strain on many open source projects to keep up. Groovy tries to run across as many JDK versions as makes sense. The more versions there are, the harder that gets. Not just in terms of potentially extra work from us but we rely on other open source tools that must also keep up to date and work consistently enough across the versions for us to be able to remain compatible. Perhaps JPMS has affected us more. Some of the changes it brought about remain on our todo list to fix before GA release of Groovy 3.0.
InfoQ: Are there plans for Groovy 3.0 to support JDK 11?
King: Groovy 3.0 already supports JDK11 unofficially. It builds and the test suite runs fine on the recent JDK11 EA builds. In terms of our Java source code compatibility, we already support var as an alias for def in local variable lambda parameters (JEP 323), so even running on JDK 8, you can define lambdas in Groovy like this:
(var x, var y) -> x.process(y)
InfoQ: What else would you like to share with our readers about Groovy 2.5 and the upcoming Groovy 3.0?
King: We are very excited with the continued growth in Groovy usage and have many more things in store that we'd like to introduce in future versions of Groovy. We are also very thankful for the patience the Groovy community has had while we complete all the features planned on our roadmap and work on some outstanding bug fixes in a few areas.
So, while we still have some engineering challenges ahead we see a great future ahead for Groovy. We are super keen for anyone who wants to contribute to come along and help out or become involved in discussions on the mailing lists.
InfoQ: What are your current responsibilities, that is, what do you do on a day-to-day basis?
King: Much of my time is spent working on Groovy either participating in discussions on mailing lists and other forums or contributing to the code base or supporting other projects which make use of Groovy.
Resources
- Groovy 2.5+ Roadmap (webinar) by Paul King
- Groovy 2.5 Features and Groovy 3+ Roadmap by Paul King (June 1, 2018)