Re: Some draft GEPs: GEP-28 and GEP-29

Jochen Theodorou <[email protected]>
Newsgroups gmane.comp.lang.groovy.devel
Message-ID <[email protected]>
Hi Paul,

(AI helped formatting)
I read GEP-28 v2 in some more detail. The new version is much clearer, 
especially the context-type table. I still have a number of questions 
around the semantics and, in particular, around the difference between 
STC and dynamic Groovy.

**1. Context type / basic resolution rule**

I think it would be useful to make one basic rule explicit: the context 
type of a shorthand should be determinable independently of resolving 
that shorthand itself. In other words, we should not need to resolve a 
shorthand in order to determine the context in which that same shorthand 
is resolved, directly or indirectly.

The Dart model seems useful as a comparison here. The initial shorthand 
is resolved from an already available context type, and subsequent 
operations are then checked from there. This makes, for example,

```groovy
X x = .RED.next(.BAR)
```

conceptually straightforward in STC: `X` gives the context for the first 
`.RED`, which gives us the receiver type, which lets us resolve `next`, 
which then gives us the context for `.BAR`.

The DCM article on Dart's dot shorthand 
https://dcm.dev/blog/2025/11/17/deeper-look-darts-dot-shorthand-hidden-complexity/ 
points out a related issue from a readability perspective: nested 
shorthands can become difficult to understand even when the compiler can 
resolve them.

**2. Method arguments and overload resolution**

For

```groovy
foo(.RED)
```

I think the STC approach is reasonable: candidate parameter types can 
provide candidate context types.

I also assume that if we have

```groovy
foo(A)
foo(B)
```

and both `A` and `B` provide a `RED`, this is an ambiguity and therefore 
a compilation error. The current GEP seems to say that.

There is another complication here, though. Groovy's notion of 
compatibility is not limited to simple subtype relationships. Numeric 
widening/conversion, boxing/unboxing and other Groovy conversions mean 
that an expression can be applicable to multiple parameter types even 
when those types are not in a simple subtype relation. The rules are 
also not exactly the same between STC and dynamic method selection.

So what exactly does it mean for `.RED` to "resolve under a candidate 
context type"? Do we only ask whether the member exists on that type, or 
do Groovy's existing conversion rules also participate? If several 
overloads provide a valid context, does existing method-selection 
ranking still distinguish them, or does the shorthand make them equally 
applicable?

This seems important because the GEP makes shorthand resolution part of 
overload selection.

For dynamic Groovy the problem is larger. There is no parameter type 
until method selection has happened. To make

```groovy
foo(.RED)
```

work dynamically, `.RED` would have to participate in method selection 
itself. We would somehow need to carry an unresolved contextual value 
through the dynamic call machinery. That is much more than a syntax 
transformation.

The fact that Dart has dot shorthand is not directly comparable here: 
Dart does not have Groovy's overload-resolution situation.

**3. Operators**

`c == .RED` seems more manageable. If `c` has a declared type, that type 
can provide the context and we can then lower the shorthand to an 
ordinary qualified reference. I understand that this is now also the 
intended behaviour in dynamic Groovy.

For STC, however, I think it is still worth describing the rule 
explicitly because Groovy's `==` ultimately involves operator/method 
semantics.

For dynamic Groovy there is a different question. `==` already has a 
more complicated runtime path than ordinary static member access. Making 
this work is probably possible because the shorthand can be resolved 
from the statically known type before runtime dispatch, but I think it 
is worth making the distinction from the `foo(.RED)` case explicit.

I am much less clear about `in`. I don't immediately see the existing 
Groovy expression for which

```groovy
x in .RED
```

would be the shorthand equivalent. What exactly is the context type for 
the RHS here? Is there a concrete use case that motivates supporting this?

Dart has a separate rule for `==`/`!=`, rather than treating them as a 
completely generic application of contextual typing, so I wonder whether 
the operator cases in GEP-28 should also be specified individually.

**4. Static fields**

For

```groovy
Duration d = .ZERO
```

the context is clearly `Duration`.

For

```groovy
def d = .ZERO
```

I assume this does not work because there is no independently known 
context type. I think this should be explicit. The fact that a local 
variable may later acquire a flow type should not provide the type 
needed to resolve its own initializer.

Likewise, for

```groovy
static final A X = .RED
```

the context is simply `A`. I don't see why the enclosing class should 
have any role in determining the shorthand.

For dynamic Groovy this seems fine as long as the context is available 
from syntax and we resolve the shorthand at compile time to an ordinary 
qualified access.

**5. Value compared with static imports**

I am also not completely convinced by the namespace-pollution argument 
for static imports.

We can write:

```groovy
import static Color.RED as MY_RED
```

so a static import can be renamed and made explicit. `.RED` cannot be 
renamed.

The real advantages of `.RED` seem to be that the qualification stays 
local, and that in some cases the type itself does not need to be 
imported because it is only present in the surrounding context. The 
same-package case seems particularly relevant here.

That may well be enough value. I am just not sure how important those 
cases are compared with the additional semantics.

**6. Stage 3 and chaining**

I find Stage 3 substantially more ambitious than the basic enum/member case.

For example:

```groovy
Duration d = .of(3)
```

is understandable if `Duration` is already known from the context.

But this adds another method-resolution step, and the GEP itself points 
out the circularity in argument positions. I think we should keep this 
separate from the basic feature until the simpler cases have been 
established.

Similarly,

```groovy
String s = .RED.next()
```

should, to me, simply fail in STC if the context is `String`. There is 
no reason to infer the type of `.RED` from `next()`.

A more interesting case is:

```groovy
X x = .RED.next(.BAR)
```

Here the compiler can resolve things in a straightforward order as long 
as `X` is the independently known context: first `X.RED`, then `next`, 
then `.BAR` from the selected parameter type.

The problem is more readability than compiler capability. With more 
nested shorthands, the reader has to reconstruct which type each dot 
refers to. The DCM article on Dart's dot shorthand is interesting in 
this respect because Dart tooling already has a lint for nested 
shorthands for exactly this kind of readability problem.

This seems to be a good candidate for "Groovy Puzzlers" type of 
problems. The compiler may be able to resolve the code quite 
unambiguously while a human reader has to perform a small type-inference 
exercise to understand it.

**7. Operators more generally**

This is also why I would be careful with the Dart comparison. Dart 
operators such as `+` are method-based as well, but the method-selection 
model is not Groovy's.

For example, something like

```groovy
X x = .FOO + .BAR
```

could involve resolving the receiver shorthand, selecting `plus(...)`, 
and then resolving the second shorthand from its parameter type. In 
Groovy, overload selection can make the type of the second shorthand 
relevant to selecting the method itself.

That is manageable for STC if the rules are well-defined, but it is a 
different problem from the Dart case.

**8. Static versus dynamic Groovy**

This is probably my main question about the proposal.

There are simple cases where the shorthand has essentially the same 
semantics in both modes because the context is directly available from 
syntax:

```groovy
Color c = .RED
```

There are other cases, most notably method arguments, where the useful 
context only exists because STC has performed method resolution.

The GEP acknowledges this split. My question is whether we really want 
to introduce general language syntax whose applicability depends so 
strongly on whether static compilation is being used.

I don't mean that every feature has to behave identically in dynamic and 
static Groovy. But for expression syntax I think we should be 
particularly careful here. The simple cases where the context is 
independently known seem much easier to justify than cases where the 
shorthand becomes part of type inference and overload resolution.

So overall, I think the next useful step would be to make the resolution 
rules and these boundaries very explicit in the GEP, in particular:

* how context types are established;
* how Groovy's existing conversion and overload-selection rules interact 
with shorthand resolution;
* which operator cases have special rules;
* where resolution is deliberately STC-only;
* and whether there is a general acyclicity rule for contextual resolution.

Cheers,
Jochen

On 8/4/26 05:40, Paul King wrote:
> Hi Matt,
> 
> 
> Thanks for your thoughtful reply. Sorry for the delay - I've been busy 
> working on releases.
> 
> 
> On the generative tooling discussion. Yes there has been some AI 
> assistance on the GEP. We "more or less" follow the ASF guidelines[1]. 
> "More or less" because there is a lot of debate at the moment:
> 
> 
>   * Does it apply to docs and code?
>   * Should we use "Generated-by" or "Assisted-by" to properly reflect
>     the guideline which indicates the committer takes responsibility for
>     the content.
>   * If the intended purpose is to potentially produce a "machine
>     parsable Tooling-Provenance file", why is AI that different to which
>     compiler I used, what editor did I code in, what checkstyle rules
>     did I use, and so forth. TBH, knowing which tools/models were used
>     has some benefit, but some folks see all the commit messages as
>     being little more than free advertising to the LLM companies.
> 
> 
> These comments aren't to indicate that we should take guidelines 
> lightly, just that we have some time while things settle down. Most 
> people seem quite good at telling when content was AI-generated/assisted.
> 
> 
> I think the most important thing for us is knowing that heavy AI 
> assistance should lead to heavy human review. And we should apply a 
> risk-based approach. AI assist on docs/tests is less risky than prod 
> code, and prod code itself has different risk profiles, e.g. a simple 
> DGM method addition vs multithreading, some of our metaprogramming code, 
> some of our type checking and bytecode gen code.
> 
> 
> On to your comments on the GEP itself, you have a pretty good 
> understanding. You pointed out some unclear bits, so I created a new 
> version which improves some of those points and added some inline 
> clarifications below.
> 
> 
> You have the general idea. Java programmers have the mental model that 
> "I already told the compiler the type of my enum in the switch clause, 
> so why should I have to tell it again in the case label".
> 
> 
> The PR extends that mental model to other places where we have already 
> told the compiler about the type (or where it could infer it), so we 
> avoid repeating that information. The leading dot also caters to dynamic 
> Groovy, which would otherwise interpret some of these cases as properties.
> 
> 
> Cheers, Paul.
> 
> 
> [1] https://www.apache.org/legal/generative-tooling.html <https:// 
> www.apache.org/legal/generative-tooling.html>
> 
> 
> On Thu, Jul 30, 2026 at 3:17 AM Matt M [email protected] 
> <mailto:[email protected]> wrote:
> 
> 
>     Hello,
> 
>     I am not sure of the GEP process, however, I figured I could chime-
>     in with some, hopefully, helpful feedback.
> 
>     I agree with the sentiment thus far: adding the dot shorthand is a
>     relatively easy improvement to the language ergonomics in some very
>     specific narrow cases. However, the syntax can/does seem a bit
>     eccentric or uncomfortable. Though, I can imagine myself getting
>     used to it over time as I am exposed to it more and more. There's a
>     certain beauty in the symmetry of it versus using a separate
>     designator or signifier instead (e.g. |:RED|).
> 
>     That said, I thought I had read through the document fairly well and
>     understood what it was getting at. However, that said, I am not
>     intimately familiar with the other languages mentioned in the GEP
>     (i.e. Dart and Swift primarily) such that I have a firm grasp of/on
>     the semantics being proposed. Therefore, I wanted to perhaps re-
>     state the GEP with my understanding of the GEP as a way to clarify
>     and perhaps provide some feedback.
> 
>     —
> 
>     One of the fundamental benefits of this feature would be avoiding
>     the need to import enum/enum-like types (static or otherwise) if/
>     when they can be inferred from their localized usage. For example:
> 
>     |LogLevel.groovy public enum LogLevel { … } Loggers.groovy public
>     void log_print(String message, LogLevel level) { … } App.groovy
>     log_print("Hello world", .INFO); |
> 
>     Such usage of the feature, in the context of the method call, is
>     only possible because of the method signature allowing the compiler
>     to know about and clearly infer the type being used and thus allows
>     the user to avoid importing the type explicitly and qualifying the
>     usage of the value with the aforementioned type.
> 
> 
> Spot on. I added a clarification in the GEP on this.
> 
> 
>     Similarly the feature only works in contexts where the type is
>     obvious and clear to the compiler (e.g. explicit declaration in/on
>     LHS expressions): |Color my_color = .RED|. The benefit here being
>     less about imports and more about avoiding/reducing redundant code
>     duplication/over specification. Coupled with compile time type-safe
>     literals over the string coercion evaluation at runtime.
> 
>     Assuming this understanding is correct, this makes sense and I can
>     see the value in each case. Albeit I am less inclined to see a /lot/
>     of value in the second, but meh neither here or there around being
>     more expressive/less verbose being an a bad thing in and of itself
>     (i.e. I would rather have the option to be less verbose than always
>     forced to be verbose).
> 
> 
> Sure, but Groovy coerces String values to enum values too, so it isn't 
> just about a better score in code golf, but actual errors that would 
> occur at runtime like: Color myColor = 'READ' could be caught at compile 
> time.
> 
> 
>     —
> 
>     Any who, I wasn't /super/ clear on all of this in the examples/cases
>     of the GEP, so it might be worth elaborating and expanding a bit
>     more. Since in my first readings, I wasn't convinced and questioned
>     how this avoids the required import anyways to even use the enum/
>     enum-like type.
> 
>     I do see a lot of value/"nice'ness" with/for being able to use this
>     feature with the static factory methods (e.g. |.of(...)|, etc.).
>     That would be really nice and just add that extra bit of nice syntax
>     sugar.
> 
>     I am still a bit hazy on how/where this shorthand would apply
>     exactly in all cases, based on reading the GEP. I am uncertain if it
>     would work in/with the |==| operator. For example, would it work for
>     |if (this.my_color == .RED) { ... }| cases or not? Would it depend
>     if |my_color| was defined as something like |public Color my_color|
>     or |public def my_color|? Again, I am not /super/ clear on this.
> 
>     The GEP seems to have a bit of contradictory notes around this. The
>     |==| example/case is mentioned a few times as being partially
>     supported early on and then it is mentioned as maybe being supported
>     in the deliverables section but then says its an excluded feature?
>     So that's why I am unsure.
> 
> 
> Yeah, that wasn't clear and I have improved it. Let me know if it still 
> seems too complex.
> 
> 
>     Finally, more of a meta note/question, was this GEP written by AI or
>     assisted with AI? There seem to quite a few places that read as if
>     it were written by AI. I am not sure what the official policy or
>     line is on the whole usage of AI is in Groovy / GEP's, but if AI is
>     involved in anyway: it would be good etiquette to explicitly,
>     clearly, and boldly mention that the GEP was AI written and/or its
>     contents' creation was assisted with AI tools. This way more
>     appropriate and applicable scrutiny may be applied, especially for
>     contradictions, inaccuracies, and/or hallucinations.
> 
>     Any who, I hope this has been helpful. Looks really cool.
> 
>     Thanks, Matt
> 
>     ------ Original Message ------ From "Paul King" [email protected]
>     <mailto:[email protected]> To "Groovy_Developers"
>     dev-GSC0n/0aZo1d/SJB6HiN2Ni2O/[email protected] <mailto:dev-GSC0n/0aZo1d/SJB6HiN2Ni2O/[email protected]> Date 7/29/2026
>     12:06:10 AM Subject Re: Some draft GEPs: GEP-28 and GEP-29
> 
>         Yeah, it will be one of those subjective things.
> 
>         Some folks think it is ugly to have to type "Color.RED" when
>         Java lets
> 
>         you just type "RED". We have that down to just one case now (or you
> 
>         can use an import) but for dynamic Groovy we probably can't do
>         better.
> 
>         The GEP proposal would give another option. And for some folks
>         all the
> 
>         extra places they can shorten using a consistent pattern might seem
> 
>         like a beautiful thing to them, but yes I still fall into the
>         camp of
> 
>         "it looks a little awkward", but I am still keen to explore the
> 
>         possibilities.
> 
>         Cheers, Paul.
> 
>         On Wed, Jul 29, 2026 at 6:25 AM MG mgbiz-yvYIh6MZAuFWk0Htik3J/[email protected]
>         <mailto:mgbiz-yvYIh6MZAuFWk0Htik3J/[email protected]> wrote:
> 
>             Just a quick feedback for GEP-28: I saw that "Sigil
>             variants" (@RED,
> 
>             :RED, etc) are already stated as rejected in the GEP, but
>             what about
> 
>             something similar to Groovy's "it" in closures (maybe as an
>             option, to
> 
>             also be able to break ambiguities without having to fall
>             back to the
> 
>             current fully qualified notation) ?
> 
>             To me the floating-in-free-air-dot-prefix notation ( .RED )
>             looks like
> 
>             an error / ugly...
> 
>             Cheers,
> 
>             mg
> 
>             Am 28.07.2026 um 08:30 schrieb Paul King:
> 
>                 Hi folks,
> 
>                 I created two more draft GEPs tentatively targeting
>                 Groovy 7:
> 
>                   * GEP-28 is about supporting DOT-notation that is
>                     supported by Dart and Swift.
> 
>                 [I like the brevity it gives but it also takes some
>                 getting used to
> 
>                 and it complicates things if Java ever develops a
>                 different compact
> 
>                 syntax.]
> 
>                   * GEP-29 is about supporting deeper null checking.
> 
>                 [This is really about whether we want to fully support
>                 JSpecify which
> 
>                 seems to be gaining momentum. NullChecker in Groovy 6
>                 already offers
> 
>                 partial support.]
> 
>                 I don't really have the cycles to do major reviews of
>                 the GEPs yet,
> 
>                 and I expect both will go through several iterations
>                 when we do. For
> 
>                 both we should ask ourselves whether we need the feature
>                 and weigh up
> 
>                 each's value against the increased complexity. But I
>                 wanted to capture
> 
>                 the ideas anyway, since I think they are worth thinking
>                 about in due
> 
>                 course. We should be comfortable with rejecting GEPs if
>                 the ideas
> 
>                 don't belong or belong somewhere else.
> 
>                 To be honest, we already have a backlog of potential
>                 Groovy 7 features
> 
>                 that will keep us busy for a while.
> 
>                 But if other folks want to jump ahead and think about
>                 them, feel free.
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.