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!

Inspired by Xe's blog

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.

Recent posts from blogs that I like

Calculating the norm of a complex number

In this quick post I'll dispel a common confusion in the basic math of complex numbers. It's often useful to calculate the norm-square (also known as absolute square) of a complex number z. This norm-square is denoted |z|^2. One could naively expect that: \[|z|^2=zz\] However, that's false! The way ...

via Eli Bendersky

Video scraping: extracting JSON data from a 35 second screen capture for less than 1/10th of a cent

via Simon Willison

The Real Country: 9 Courses and crop rotation

Arable farmers learned to rotate crops, to prevent loss of soil fertility. At the same time, land was enclosed to remove it from use for communal grazing.

via The Eclectic Light Company