Hi folks,
I'd like some feedback on GROOVY-12115 / PR #2645, specifically on the
config knob and its default. Quick framing first.
The problem: Because of erasure, various generic APIs require callers
to supply a type the compiler already knows. Classic example:
@CompileStatic
def demo(List<String> list) {
def checked = list.asChecked(String) // String is redundant -
the receiver is already List<String>
}
The fix: A new parameter annotation `@ClassTag` lets the static
compiler synthesize and inject that `Class<T>` argument from the
receiver's type arguments, so under `@CompileStatic`/`@TypeChecked`
you can just write:
def checked = list.asChecked() // compiler supplies String.class
Synthesis only fires when the type is statically known (no `def`, raw,
or wildcard-only receivers). For this to work, we retrofit the
asChecked method by annotating the parameter we want to "disappear"
and be supplied automatically with `@ClassTag`. Prime retrofit
candidates are `asChecked` and `withDefault` / `withCheckedDefault`.
So far so good. But there are two flavours:
1. New/opt-in overloads - uncontroversial, you're choosing the tagged
method explicitly.
2. *Preemption* - this handles the case where both a tagged method and
non-tagged method could both match. If we automatically preferred the
tagged method, this would be a silent behaviour change, but it is
exactly what we sometimes want. So, it's currently gated behind a
config allowlist: `CompilerConfiguration.classTagPreemptionTargets`,
defaulting to just `withDefault`.
So the questions:
- Is a config-level allowlist the right mechanism for opting methods
into preemption, or would you rather preemption not exist at all
(opt-in overloads only)?
- If we keep it, is `withDefault` the right - and only - default?
My gut feel is leave as is, we can always add a system property or
dedicated commandline option later. And I think just "withDefault" to
start with. Anyone designing new APIs can just factor in ClassTag from
the start - it will only be where existing APIs exists and folks want
to retrofit the "reified" shorthand after the fact where more
allowlist items would be needed.
One forward-looking note: `@ClassTag` deliberately only reifies the
erased `Class`, which is all today's `isInstance`-style consumers
need. A fuller "TypeTag" tier carrying the complete generic signature
(à la Scala) could be a Groovy 7 conversation, but it's out of scope
here.
PR: https://github.com/apache/groovy/pull/2645
JIRA: https://issues.apache.org/jira/browse/GROOVY-12115
Cheers,
Paul