Inconsistency About Omitting 'return' Keyword to Return Closure Block in Groovy

Viewed 81

I've just started learning Groovy. And I got into this: https://groovy-lang.org/style-guide.html#_return_keyword_optional So, 'return' can be omitted at the end in a method definition. Alright. Even though I would prefer using it personally, I wanted to take a look. I tested some simple cases to figure out how it works:

e.g. demo.groovy:

def a1 = { return { return 'hi'} }()
println a1

def a2 = { return {'hi'} }()
println a2

def a3 = { {'hi'} }()
println a3

def a4 = { {-> 'hi'} }()
println a4

I ran the script, and the result was:

demo$_run_closure1$_closure5@21c64522
demo$_run_closure2$_closure6@460f76a6
hi
demo$_run_closure4$_closure7@1922e6d

Starting from the case a1, I omitted keywords 'return' one at a time. They are the cases a2, and a3. What made me scratch my head is the case a3. I expected the closure to be returned like the cases a1 and a2. But it returned 'hi', not the closure itself. So I added another test case a4 and then it worked as expected.

I tried to search some documentation about it, but couldn't find a proper one. Why does it return 'hi' not a closure object?

Thanks in advance.

FYI, my groovy version is:

Groovy Version: 3.0.7 JVM: 15.0.1 Vendor: Oracle Corporation OS: Mac OS X

1 Answers

What you see in the a3 example is not a nested closure, but a nested code block instead.

Nested code blocks

An infrequently used structure within Java is the anonymous code block. It’s generally not encouraged as it’s often a sign that refactoring the related code into a method is in order. But it’s sometimes useful to restrict scoping and is now available in Groovy:

{
    def a = 1
    a++
    assert 2 == a
}
try {
    a++ // not defined at this point
} catch(MissingPropertyException ex) {
    println ex.message
}
{
    {
        // inner nesting is another scope
        def a = 'banana'
        assert a.size() == 6
    }
    def a = 1
    assert a == 1
}

Be aware though that in Groovy having a code block looking structure after any method call will be seen as an attempt to pass a closure as the last parameter in the method call. This happens even after a new line. So it’s safe to start an anonymous code block after any other block (e.g. an if-then-else statement or another anonymous code block). Anywhere else and you might need to terminate the previous statement with a semicolon. In which case, see the note above about refactoring your code! :-)


Source: https://groovy-lang.org/releasenotes/groovy-3.0.html#_nested_code_blocks

You can run this script in the groovyConsole and go to "Script" -> "Inspect AST" to see what the compiler produces in this case:

enter image description here

You can see that a3 is a closure that implicitly returns 'hi'.

The support for nested code blocks was added in Groovy 3. If you run the same example in Groovy 2, you would see the following compilation error:

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
/home/wololock/workspace/groovy-3-sandbox/src/main.groovy: 7: Ambiguous expression could be either a parameterless closure expression or an isolated open code block;
   solution: Add an explicit closure parameter list, e.g. {it -> ...}, or force it to be treated as an open block by giving it a label, e.g. L:{...} @ line 7, column 12.
   def a3 = { {'hi'} }()
              ^

1 error
Related