Hi Paul, I've been meaning to read through this message since you put it out there since you asked for discussion and feedback. I was busy with the some other stuff (as you can guess what with).
I did an initial read through the assessment and quick glance through the code. It seems fine. I think the heuristic trick for determining if something is a cast or an operation is quite clever. Perhaps too clever though when someone runs into it unexpectedly. The two example cases mentioned where it _doesn't_ work (e.g. `ASDF` the variable and `myClass` the type) seem to invite fragility and shadow semantics based language design. I wonder if this is the tradeoff of having smaller but sharper edge cases for this issue. Conversely, from my reading, it looked like the idea of having the downstream parser fix the issue internally was flagged as being a bit involved when it was first put forth. However, I wonder if it might be worth revisiting that idea again and seeing if it's still viewed the same way. If we look at this issue from the end-user perspective: the compiler knows (or should and _will_ know, eventually) which symbols are types (i.e. valid cast targets) and which symbols are everything else (i.e. valid operations targets - baring using keywords or something). In other words, it shouldn't need to be something that the user needs to care about, has to figure out in their design, or keep in mind when they are writing their code. Groovy doesn't give off that impression or design goal. The question they would then themselves ask is: Why can't the compiler figure out that `ASDF` is my variable and `myClass` is my type? (i.e. why does how I name my types exactly matter in a typed language) >From a end-user perspective, Groovy has types and is able to be smarter than other languages. The compiler has the information and the language has the semantics (i.e. it's not an un-typed language), such that it should be smart enough to disambiguate what the user is doing and what they meant. I would postulate such at least. This is my take on it at least. I don't think the heuristics solution is wrong per se. I just wonder if it's more fragile and a "gotcha" point for the language's shadow semantics now (i.e. types _can_ be named whatever you want, but not _really_ and depending on your naming choices cause parts of the language to not work (i.e. a kludge I am sure someone will extremely frustrated running into)). I know you mentioned that this is a step forward and not meant to cover all cases. If all else, it's at least progress and I am sure that's better than nothing :) Thanks, Matt ------ Original Message ------ >From "Paul King" <[email protected]> To "Groovy_Developers" <[email protected]> Date 8/19/2026 1:12:45 AM Subject [DISCUSS] GROOVY-10355: parenthesized names parsed as cast types > > >Hi folks, > > > >Since Groovy 3, the Parrot parser reads a parenthesized bare name > >followed by certain operators as a cast type rather than a value. > >There are good reasons why we want exactly this in numerous cases, but > >there are other cases where the expectation might be how they were > >parsed in Groovy 2 or in Java. > > > >For example, all of these fail with "unable to resolve class" today, > >though all worked in Groovy 2: > > > > def r = "A" + (b) + "C" // parsed as cast of (+ "C") to type b > > def r = (answer) - 3 > > def r = (map.x) + 2 > > def r = (a) in [1, 2] // (a) in b also fails, but only in > > // some statement contexts, not others > > > >But there are gains from the Parrot approach that we wouldn't want to > >back out of now. So, these all work today, and we would want them to > >keep working unchanged: > > > > (int) -1 // primitive casts are exempt from the > > (long) -3 // proposed rule (distinct token type) > > (Integer) -1 // cast of a negative literal > > (Runnable) { ... } // SAM coercion > > (Point) [3, 4] // list-to-constructor coercion > > > >There's an assessment doc covering the ticket cluster (GROOVY-9864, > >GROOVY-8913, GROOVY-10724, GROOVY-11320), the options considered and > >declined, and a proposed fix targeting 6.0: restore the value reading > >when the parenthesized name's final segment starts lowercase, plus a > >better error message for the cases that stay cast-first, e.g.: > > > > unable to resolve class ASDF; '(ASDF)' followed by an operand is > > parsed as a cast - if 'ASDF' was meant as a value, wrap it in a > > second set of parentheses, e.g. ((ASDF)) > > > >Implementation for review: https://github.com/apache/groovy/pull/2817 > >Ticket: https://issues.apache.org/jira/browse/GROOVY-10355 > >Assessment doc is attached as a PDF on the ticket. > > > >Feedback welcome, particularly on the capitalization-based rule and on > >what should happen to shapes like (a)[b] which have never worked in > >any Groovy version. The ticket proposes a step forward but not all > >cases. > > > >Cheers, Paul.
