TIL: Closures in Groovy
I interact with Groovy solely because Jenkins uses it to define pipelines.
Pros:
- It’s in the Java ecosystem
- It’s a “real” language (turing complete unlike YAML)
- It’s not YAML
Cons:
- No static typing
- Linters/formatters aren’t great
- Jenkins is quite hard to work with and has surprisingly poor tooling
While updating some Groovy scripts, I wanted to update a utiltiy method to take a closure in a nicer way. The method looked like this:
def hello(clo, first = "John", last = "Doe") {
println "Hello, $first $last"
clo()
}
// usage
foo(clo: { println "I didn't see you there!" }, first: "Jerred", last: "Shepherd")
I’ve seen methods that look a bit prettier when called, like this:
def bar(someArg, Closure clo) {
clo()
}
bar(someArg) {
println 'hello'
}
Note that the closure is passed as the last argument. This allows the closure to be passed in braces after the method call. This looks quite a bit nicer!
Originally, I wanted to combine named parameters, default parameters, and a closure as the last argument. Unfortunately, this doesn’t seem possible. Here’s what I came up with:
def baz(first = "John", last = "Doe", Closure clo) {
println "Hello, $first $last"
clo()
}
The result of running this:
baz(first: "Jerred", last: "Shepherd") {
println "I didn't see you there!"
}
Hello, [first:Jerred, last:Shepherd] Doe
I didn't see you there!
Groovy implements named parameters as a map. Unfortunately, it seems that Groovy is using the both the first
and last
parameters as a map value and passing that to the first
argument.
// Groovy takes this:
baz(first: "Jerred", last: "Shepherd")
// and implicitly converts it to this:
baz([first: "Jerred", last: "Shepherd"], null)
// so, when calling baz, we're passing the map as the first argument and null as the second argument
// this leads to Groovy using the map as the first argument, and the default value of "Doe" as the second argument
Hello, [first:Jerred, last:Shepherd] Doe
I didn't see you there!
// instead, we want Groovy to print:
Hello, Jerred Shepherd
I didn't see you there!
Closures are still pretty cool, but it’s frustrating to figure out the syntax for how some of these features interact. Additionally, the feedback loop for Groovy with Jenkins is long, so if you don’t know the language, it’s hard to make progress.