> a type-aware fallback: use the array reading, except when the initializer is
> empty or has a single element and the leaf component type is Closure, a SAM
> type, or possibly Object
One limitation is that the parser does not have access to resolved types, so it
cannot make a type-based decision. It is difficult to map "Type[] a = {}" to
the closure case until Type is resolved. By that point, the parsing drawbacks
have already been experienced. Only the simplest closure literals have been
represented in the AST as something array-like that can be converted after
ResolveVisitor.
It would be good to know the frequency of such promotion usages. My gut says
it is extremely rare and so the benefits for typical cases allow for a breaking
change for an edge case.
________________________________
From: Daniel Sun <[email protected]>
Sent: Wednesday, October 28, 2026 10:36 AM
To: [email protected] <[email protected]>
Subject: Re: Java array initializer compatibility and breaking change for
closure promotion edge case
Hi Eric, Thanks for raising this on the list, and for the work in https:
//urldefense. com/v3/__https: //github.
com/apache/groovy/pull/3054__;!!GFN0sa3rsbfR8OLyAw!ZWGYSb4TJaFetP8H8F0To37ZsRWw72txXjZ7ryqMC8v2k1O5aO1NvsdNLoTRg_Oi7W5h6r0CXpJXQcuEn61J7w$.
Hi Eric,
Thanks for raising this on the list, and for the work in
https://urldefense.com/v3/__https://github.com/apache/groovy/pull/3054__;!!GFN0sa3rsbfR8OLyAw!ZWGYSb4TJaFetP8H8F0To37ZsRWw72txXjZ7ryqMC8v2k1O5aO1NvsdNLoTRg_Oi7W5h6r0CXpJXQcuEn61J7w$.
I agree that terse Java-style array initializers such as
`int[][] matrix = { {1}, {2}, {3} }` are worth adding. They close a
real gap for people who paste Java snippets, and the non-closure cases
look solid.
On the GROOVY-8561 edge, I am less worried about the empty form
`Closure[] a = {}` becoming a zero-length array than about non-empty
arrowless bodies. Today those store a Closure; under the Java-faithful
reading they can run at initialisation and leave null (or another
scalar) in the array. For example:
Closure[] a = { println('x') }
prints at declaration time and stores [null]. That is a silent
behaviour change, which is harder for callers to detect than a hard
failure. The same class of issue shows up for some Runnable/Callable
(and similar SAM) bodies that are not command chains. COMPATIBILITY
currently documents the empty-{} flip well; it does not yet spell out
this eager-evaluation case.
Arrow forms ({ -> }, { x -> ... }) remain unambiguous, so the escape
hatch for an intentional single-closure array is still available. I do
not think we need to keep array promotion for every arrowless {}
forever, but I would prefer not to break working Closure/SAM/Object
cases quietly.
Two directions that seem workable:
1. Keep the Java-faithful reading, with a stronger COMPATIBILITY note,
an explicit test matrix for {} / { it } / { -> } / { { } } /
Runnable[] / Object[] / @CompileStatic, and ideally a compile-time
diagnostic when a Closure/SAM-typed array element is not clearly a
closure, lambda, or method reference (accepting that dynamic code
cannot be exact, e.g. { getHandler() }).
2. Prefer a type-aware fallback: use the array reading, except when
the initializer is empty or has a single element and the leaf
component type is Closure, a SAM type, or possibly Object — in
those cases keep today's closure promotion. Multi-element and
nested initializers would always be arrays. Nothing that works
today breaks, while Java users still get the forms they actually
paste (String[], int[], int[][], …).
My preference is (2), unless the list is comfortable with (1) plus a
clear diagnostic story. Either way, pinning the chosen semantics with
tests would help a lot.
Happy to follow whatever the list settles on, and to re-review the PR
once the Closure/SAM story is settled.
Cheers,
Daniel Sun
On 2026-10-28 13:53 UTC Milles, Eric (TR Technology) via dev wrote:
> We have been stepping in Java-style array syntax starting in Groovy 3. The
> next step of this support is the terse initialization syntax:
>
> int[][] matrix = { {1}, {2}, {3} }
>
> Without this change, a multi-dimensional array expression requires "new
> int[]" for each sub-array. Or falling back to square-bracket notation.
>
> As discussed in GROOVY-8561, there is one edge case that results in a
> breaking change. When a single value is provided for an array declaration,
> the value is wrapped in an array implicitly: int[] a = 1
>
> The conflict comes when the type supports a closure literal:
> Closure [] a = {}
> Runnable[] b = {}
> String [] c = { print(it) } // not sure what this is for
>
> This can be disambiguated with the addition of an arrow:
> Closure [] a = { -> }
> Runnable[] b = { -> }
> String [] c = { x -> print(x) }
>
> I have made note of this in the COMPATIBILITY document and I don't think this
> pattern is in common usage.
>
> Do you have any comments on the risk of this breaking change? Or do you
> think it is necessary to support array promotion for arrowless closures?
>
> This change is implemented here:
> https://urldefense.com/v3/__https://github.com/apache/groovy/pull/3054__;!!GFN0sa3rsbfR8OLyAw!ZWGYSb4TJaFetP8H8F0To37ZsRWw72txXjZ7ryqMC8v2k1O5aO1NvsdNLoTRg_Oi7W5h6r0CXpJXQcuEn61J7w$
> And was originally proposed here:
> https://urldefense.com/v3/__https://github.com/apache/groovy/pull/691__;!!GFN0sa3rsbfR8OLyAw!ZWGYSb4TJaFetP8H8F0To37ZsRWw72txXjZ7ryqMC8v2k1O5aO1NvsdNLoTRg_Oi7W5h6r0CXpJXQcs2zLUWEA$
> This e-mail is for the sole use of the intended recipient and contains
> information that may be privileged and/or confidential. If you are not an
> intended recipient, please notify the sender by return e-mail and delete this
> e-mail and any attachments. Certain required legal entity disclosures can be
> accessed on our website:
> https://www.thomsonreuters.com/en/resources/disclosures.html
>