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.

Reply via email to