Paul King created GROOVY-12217:
----------------------------------
Summary: Decide on a fat-free twin for sum (positional collision
with the initialValue overload)
Key: GROOVY-12217
URL: https://issues.apache.org/jira/browse/GROOVY-12217
Project: Groovy
Issue Type: Sub-task
Reporter: Paul King
h3. Summary
Split out of the fold/reduce sub-task (GROOVY-12205 scope). Unlike every other
fat-free method, {{sum}}'s functional twin cannot be added mechanically: it
would land on the same single trailing-argument slot as the existing
{{sum(Iterable, Object initialValue)}} overload, changing overload resolution
for some existing calls. This item is for the compatibility decision before any
implementation.
h3. Why sum is different
{{sum}} overloads its single trailing argument between two meanings:
* {{sum(Iterable, Object initialValue)}} — sum the elements starting from
{{initialValue}}.
* {{sum(Iterable, Closure)}} — map each element, then sum.
The natural twin of the second is {{sum(Iterable, Function<? super T, ?>)}}.
But a {{Function}} is an {{Object}} and is more specific, so any argument
assignable to {{Function}} would now bind to the mapping twin instead of the
{{initialValue}} overload. This is unavoidable with a same-name, same-arity
twin.
(By contrast, {{inject}}/{{injectAll}} keep the reduce function in a distinct
position after an explicit {{initialValue}} parameter, so they have no such
collision.)
h3. Practical blast radius
The reroute only affects arguments that are {{Function}} instances. Passing
such a value as {{sum}}'s initial value already fails today:
{{[1,2,3].sum(someFunction)}} throws {{MissingMethodException: No signature of
method: plus for class: ...Function}} (a {{Function}} has no {{plus}}). So the
rerouted calls are ones that are already broken, not working code. The only
genuine regression would be an object that is simultaneously a {{Function}} and
a valid summation seed (defines {{plus}}) — contrived, but not provably
impossible in a large codebase.
h3. Methods that would be affected (if we proceed)
{{sum}} forms lacking a twin (6): {{Iterable}}, {{Iterator}}, each with and
without {{initialValue}}, {{T[]}}, {{T[]}}+init. Proposed type: {{Function<?
super T, ?>}}.
h3. Options
# *Proceed*, justified by "rerouted calls already throw today", with a targeted
grep of the codebase + ecosystem and a one-release changelog note.
# *Defer* to a dedicated compatibility pass (recommended) — keeps the
mechanical batches clean and gives {{sum}} a proper review, possibly with a
deprecation/transition path.
# *Exclude* the {{sum}} mapping twin entirely — arguably fits GROOVY-12205's
own "unless the JDK already provides the equivalent" carve-out, since a mapped
sum is expressible as {{list.stream().mapToInt(f).sum()}} or
{{list.collect(f).sum()}}.
h3. Recommendation
Defer (option 2). The collision is unique to {{sum}}, the twin cannot be added
without changing resolution semantics, and the value of the twin is low given
the JDK stream alternatives.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)