Hi Paul,
1. I am a bit worried about the dual use of literal map syntax and
object decomposition in Groovy:
1. In the "def sum(xs) ..." example xs is list (of Number|s), and
"case []" is the branch that handles an empty list.
2. Whereas in the "switch (event) ..." example the " case [type:
'click', ...]" branch does not compare to a map, but
decompositions the event object into its type, etc parts.
3. While seeing any object in Groovy as a map of properties has
been around forever, it does already clash with peoples'
expectations sometimes when the object /is /a Map (violating
least surprise).
4. So I was wondering if introducing a seperate syntax for object
decomposition which sets it apart from literal map syntax would
make sense... ?
2. In "case [type: 'click', x: var x, y: var y]" the "x: var x" parts
looks unnecsessarily verbose/Java-ish to me - can/should we allow
just "x" here, i.e. "case [ type: 'click', x, y ] -> handleClick(x, y)"
1. Or, in case a mapping shall occur, can we drop the "var",
i.e. "case [ type: 'click', widthPos:x, heightPos:y ] ->
handleClick(x, y)"
1. (This might tie in with my suggestion above: This might
only work if there is an explicit syntax for a decomposition
pattern that does not look like a map literal.)
Cheers,
mg
def sum(xs) {
switch (xs) {
case [] -> 0
case [var h, var... t] -> h + sum(t)
}
}
switch (event) {
case [type: 'click', x: var x, y: var y] -> handleClick(x, y)
case [type: 'key', code: var k] -> handleKey(k)
case [type: 'scroll', ...] -> redraw()
}
Am 27.04.2026 um 13:39 schrieb Paul King:
Hi folks,
I believe the following features are ready for inclusion in Groovy
6.0.0-alpha-1.
Please see the relevant PRs and Jira issues for more information.
Feedback, as always, is welcome.
Cheers, Paul.
GEP-18 -- Ported GPars features
================================
1. Parallel collections inside a scope -- drop-in speedup
ParallelScope.withPool(Pool.cpu()) {
urls.collectParallel { fetch(it) }
.findAllParallel { it.status == 200 }
.groupByParallel { it.host }
}
Familiar GPars xxxParallel names, pool-isolated and
virtual-thread-ready.
2. Dataflows -- orchestration by data dependency, not thread
choreography
def df = new Dataflows()
async { df.result = combine(df.user, df.prefs) }
async { df.user = loadUser(id) }
async { df.prefs = loadPrefs(id) }
println await(df.result)
The order in source is irrelevant; readers block until the
writers bind. Reads as plainly as sequential code.
But also, actors/active objects, agents and channels.
GEP-20 -- Additional multi-assignment forms
============================================
1. Tail rest against a Stream -- head/body split with no
materialisation
Files.lines(path).withCloseable { src ->
def (header, *body) = src
body.filter { !it.isBlank() }.forEach { process(it) }
}
Path A keeps the Stream lazy and propagates onClose -- the
killer demo for the streaming contract.
2. Head and middle rest -- edge-aware slicing in one line
def (first, *middle, last) = readings // peel both ends
def (l, *m, r) = segment // boundary + interior
Replaces [0] / [1..-2] / [-1] boilerplate that's surprisingly
common.
3. Map-style destructuring -- beans and config in one shot
def (host: h, port: p, name: n) = config
def (name: String fullName, age: int a) = person
Works against Map, beans, and GroovyObject uniformly via the
MOP -- the rename form (name: fullName) is a quiet bonus.
When assessing GEP-20, bear in mind the very early draft for GEP-19
targetted tentatively for Groovy 7.
GEP-19 -- Structural pattern matching in switch
================================================
1. Recursive list algorithm in its canonical functional shape
def sum(xs) {
switch (xs) {
case [] -> 0
case [var h, var... t] -> h + sum(t)
}
}
The motivating example -- what you can't write tidily today.
2. Map patterns for tagged-shape dispatch (events, JSON, configs)
switch (event) {
case [type: 'click', x: var x, y: var y] -> handleClick(x, y)
case [type: 'key', code: var k] -> handleKey(k)
case [type: 'scroll', ...] -> redraw()
}
Open semantics plus rest binding makes this read like a
protocol spec.
3. Type pattern + record pattern + guard -- visitor replacement
switch (shape) {
case Circle c when c.radius > 0 -> Math.PI * c.radius**2
case Rect(int w, int h) -> w * h
case Line(_, Point p2) -> "ends at $p2"
default -> 0
}
One construct subsumes instanceof chains, accessor calls, and
the constraint check.