You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2026/04/12 06:19:03 UTC

(groovy-website) branch asf-site updated (78c5cba -> e0828fc)

This is an automated email from the ASF dual-hosted git repository.

paulk pushed a change to branch asf-site
in repository https://gitbox.apache.org/repos/asf/groovy-website.git


    from 78c5cba  add GROOVY-5373, GROOVY-11921, GROOVY-11915, GROOVY-11919, GROOVY-11922
     new 939b01c  add 5.0.5 and 4.0.31 Windows Installers
     new e0828fc  compound assignment operator overloading proposal

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 site/src/site/sitemap-dev.groovy |  14 +--
 site/src/site/wiki/GEP-15.adoc   | 195 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 197 insertions(+), 12 deletions(-)
 create mode 100644 site/src/site/wiki/GEP-15.adoc


(groovy-website) 01/02: add 5.0.5 and 4.0.31 Windows Installers

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/groovy-website.git

commit 939b01cac2b620585fe821830895d8180a610ff2
Author: Paul King <pa...@asert.com.au>
AuthorDate: Sun Apr 12 16:02:12 2026 +1000

    add 5.0.5 and 4.0.31 Windows Installers
---
 site/src/site/sitemap-dev.groovy | 14 ++------------
 1 file changed, 2 insertions(+), 12 deletions(-)

diff --git a/site/src/site/sitemap-dev.groovy b/site/src/site/sitemap-dev.groovy
index fbedf03..0c2bfc1 100644
--- a/site/src/site/sitemap-dev.groovy
+++ b/site/src/site/sitemap-dev.groovy
@@ -89,12 +89,7 @@ downloads {
         }
         version('5.0.5') {
             stable true
-//            windowsInstaller 'https://groovy.jfrog.io/artifactory/dist-release-local/groovy-windows-installer/groovy-5.0.5/'
-        }
-        version('5.0.4') {
-            stable true
-            archive true
-            windowsInstaller 'https://groovy.jfrog.io/artifactory/dist-release-local/groovy-windows-installer/groovy-5.0.4/'
+            windowsInstaller 'https://groovy.jfrog.io/artifactory/dist-release-local/groovy-windows-installer/groovy-5.0.5/'
         }
     }
     distribution('Groovy 4.0') {
@@ -106,12 +101,7 @@ downloads {
         }
         version('4.0.31') {
             stable true
-//            windowsInstaller 'https://groovy.jfrog.io/artifactory/dist-release-local/groovy-windows-installer/groovy-4.0.31/'
-        }
-        version('4.0.30') {
-            stable true
-            archive true
-            windowsInstaller 'https://groovy.jfrog.io/artifactory/dist-release-local/groovy-windows-installer/groovy-4.0.30/'
+            windowsInstaller 'https://groovy.jfrog.io/artifactory/dist-release-local/groovy-windows-installer/groovy-4.0.31/'
         }
     }
     distribution('Groovy 3.0') {


(groovy-website) 02/02: compound assignment operator overloading proposal

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/groovy-website.git

commit e0828fca609ba2de79856aec2079f15b32a81a82
Author: Paul King <pa...@asert.com.au>
AuthorDate: Sun Apr 12 16:18:54 2026 +1000

    compound assignment operator overloading proposal
---
 site/src/site/wiki/GEP-15.adoc | 195 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 195 insertions(+)

diff --git a/site/src/site/wiki/GEP-15.adoc b/site/src/site/wiki/GEP-15.adoc
new file mode 100644
index 0000000..9bc2742
--- /dev/null
+++ b/site/src/site/wiki/GEP-15.adoc
@@ -0,0 +1,195 @@
+= GEP-15: Compound assignment operator overloading
+
+:icons: font
+
+.Metadata
+****
+[horizontal,options="compact"]
+*Number*:: GEP-15
+*Title*:: Compound assignment operator overloading
+*Version*:: 1
+*Type*:: Feature
+*Status*:: Draft
+*Leader*:: Paul King
+*Created*:: 2026-04-12
+*Last modification*&#160;:: 2026-04-12
+****
+
+== Abstract
+
+Groovy supports operator overloading: `\+` maps to `plus()`, `-` maps to `minus()`,
+`<<` maps to `leftShift()`, and so on. However, compound assignment operators
+(`pass:[+=]`, `-=`, `pass:[<<=]`, etc.) are always desugared to `x = x.op(y)` -- there is
+no way to override `+=` independently of `pass:[+]`. This GEP proposes adding
+support for dedicated compound assignment methods such as `plusAssign`,
+`minusAssign`, `leftShiftAssign`, etc.
+
+=== Motivation
+
+The current desugaring of `x += y` to `x = x.plus(y)` creates a new object
+and reassigns the variable. This has several drawbacks:
+
+* **Mutable data structures** are forced into a create-and-reassign pattern
+  when in-place mutation is the intended semantics. For example, a mutable
+  list's `+=` creates a new list rather than appending in place.
+* **Final fields and variables** cannot use compound assignment at all,
+  even when the underlying object is mutable and supports in-place mutation.
+* **Intent** is unclear: the class author cannot distinguish between
+  `x + y` (produce a new value) and `x += y` (mutate in place).
+
+Languages like Kotlin and Scala already support this distinction.
+Kotlin maps `\+=` to `plusAssign()` when available, falling back to
+`plus()` with reassignment. Scala allows mutable collections to define
+`+=` directly.
+
+=== Requirements
+
+* Support dedicated compound assignment methods (`plusAssign`, `minusAssign`, etc.)
+  that are called in preference to the current `plus` + reassign pattern.
+* Maintain full backward compatibility: existing code that uses `+=` with
+  `plus()` must continue to work identically when no `plusAssign` method exists.
+* Support `+=` on `final` fields/variables when `plusAssign` is available.
+* Work correctly in both `@CompileStatic` and dynamic Groovy.
+
+==== Non-goals
+
+* Changing the behavior of `++` and `--` operators (these use `next()`/`previous()`
+  and are conceptually different).
+* Changing subscript compound assignment (`a[i] += b`), which uses the existing
+  `getAt`/`putAt` pattern.
+
+=== Operator method name mapping
+
+[cols="1,2,2",options="header"]
+|===
+| Operator | Assign method | Fallback method
+
+| `+=` | `plusAssign` | `plus`
+| `-=` | `minusAssign` | `minus`
+| `*=` | `multiplyAssign` | `multiply`
+| `/=` | `divAssign` | `div`
+| `%=` | `modAssign` | `mod`
+| `%%=` | `remainderAssign` | `remainder`
+| `**=` | `powerAssign` | `power`
+| `<\<=` | `leftShiftAssign` | `leftShift`
+| `>>=` | `rightShiftAssign` | `rightShift`
+| `>>>=` | `rightShiftUnsignedAssign` | `rightShiftUnsigned`
+| `&=` | `andAssign` | `and`
+| `\|=` | `orAssign` | `or`
+| `^=` | `xorAssign` | `xor`
+| `//=` | `intdivAssign` | `intdiv`
+|===
+
+=== Resolution algorithm
+
+When the compiler encounters `x += y`:
+
+1. Look for a method `plusAssign(y)` on the type of `x`.
+2. If found, call `x.plusAssign(y)` directly. No reassignment occurs.
+   This works even when `x` is `final`.
+3. If not found, fall back to the current behavior: `x = x.plus(y)`.
+   This requires `x` to be reassignable.
+4. If `x` is `final` and no `plusAssign` exists, report a compile error
+   (in `@CompileStatic` mode).
+
+When both `plusAssign` and `plus` exist on the same type, `plusAssign`
+takes precedence. This is the pragmatic choice for Groovy since it lacks
+Kotlin's `val`/`var` distinction. If a class author defined `plusAssign`,
+they intended it to be used for `+=`.
+
+=== Design considerations
+
+* **Primitives are unaffected.** `int x = 0; x += 1` continues to use
+  the existing fast-path for primitive arithmetic. The `plusAssign` lookup
+  only applies when the base operator would resolve to a method call.
+* **Expression value.** When `plusAssign` is called, the expression value
+  of `x += y` is `x` (the mutated object), not the return value of
+  `plusAssign`. This differs from the current behavior where the expression
+  value is the result of `x.plus(y)`.
+* **Extension methods.** `plusAssign` is discoverable as an extension method
+  (DGM or category), not just as an instance method.
+* **`@OperatorRename` support.** The existing `@OperatorRename` annotation
+  will be extended to support renaming the assign variants, e.g.
+  `@OperatorRename(plusAssign="addInPlace")`.
+
+=== Examples
+
+A mutable accumulator:
+
+[source,groovy]
+----
+class Accumulator {
+    int total = 0
+    void plusAssign(int n) { total += n }
+    Accumulator plus(int n) { new Accumulator(total: total + n) }
+}
+
+def acc = new Accumulator()
+acc += 5          // calls acc.plusAssign(5), mutates in place
+assert acc.total == 5
+
+def acc2 = acc + 3 // calls acc.plus(3), returns new Accumulator
+assert acc2.total == 8
+assert acc.total == 5
+----
+
+A final field with in-place mutation:
+
+[source,groovy]
+----
+@CompileStatic
+class EventBus {
+    final List<String> listeners = []
+    // List has leftShiftAssign via extension method or subclass
+}
+
+def bus = new EventBus()
+bus.listeners <<= "listener1"  // calls listeners.leftShiftAssign("listener1")
+----
+
+=== Static compilation (`@CompileStatic`)
+
+In `@CompileStatic` mode, the type checker resolves `plusAssign` at
+compile time using `findMethod()`. If found, it records the target method
+on the expression via node metadata and the bytecode generator emits a
+direct method call -- no dup, no store-back.
+
+If not found, the existing desugaring to `x = x.plus(y)` applies.
+
+=== Dynamic Groovy
+
+In dynamic Groovy, the runtime checks for `plusAssign` via the Meta-Object
+Protocol (MOP). If `respondsTo(target, "plusAssign", arg)` succeeds, it is
+called. Otherwise, the fallback to `plus` + reassignment applies.
+
+This requires a new runtime helper method (e.g., in `ScriptBytecodeAdapter`)
+that encapsulates the try-assign-then-fallback logic.
+
+=== Breaking behavior
+
+1. **Existing `plusAssign` methods.** If a class already defines a method
+   literally named `plusAssign`, `+=` will now call it instead of desugaring
+   to `plus` + reassign. This is unlikely in practice but must be documented
+   in release notes.
+2. **Expression value change.** `val = (x += y)` -- with `plusAssign`, the
+   captured value is `x` (the mutated object), not the result of `plus`.
+   This only affects code that uses compound assignment as an expression.
+3. **Final fields.** In `@CompileStatic`, `final` fields with a type that
+   has `plusAssign` can now use `+=`. Previously this was always an error.
+
+== References and useful links
+
+* https://kotlinlang.org/docs/operator-overloading.html#augmented-assignments[Kotlin augmented assignments]
+* https://docs.scala-lang.org/overviews/collections-2.13/overview.html[Scala mutable collection operators]
+
+=== Reference implementation
+
+_TBD_
+
+=== JIRA issues
+
+_TBD_
+
+== Update history
+
+1 (2026-04-12) Initial draft