Hi Jochen, In the first instance, for that piece of the GPars functionality, the goal was to just implement the simplest slice of the existing methods from GPars, e.g. collectParallel and the other approx 20 similarly named methods. I didn't contemplate a rename to retain backwards compatibility.
I agree it would be nice to have more of the original "DSL", and you are right that while it is true that we have jettisoned JSR-166, that technology isn't a requirement for the DSL. For me, there was no obvious way to implement them that didn't seem like it needed further investigation. We have the other GPars mechanisms that could be put to use instead for that DSL piece, namely ParallelEnhancer, makeConcurrent() , makeSequential(), and asConcurrent(). However, I haven't had time to consider whether Groovy's new async features introduce complex scenarios when applying dynamic metaprogramming. Nor yet whether this lies strictly in the Groovy DSL space or some parts could fall through to the functionality in the Java-only jar. Also, since we now implement the functionality using JDK streams, the collect method name clash while rare previously might become more of an issue. TL;DR: let's give users a coherent slice of the existing functionality and work on more pieces over time. Cheers, Paul. On Fri, May 1, 2026 at 1:37 AM Jochen Theodorou <[email protected]> wrote: > actually one question regarding GPars features. > > Afaik you could do urls.collectParallel{...}, but also > urls.parallel.collect{...}. > > I guess it was excluded because of getParallel() as "Legacy JSR-166 > ParallelArray API; obsolete since Java 8 streams" > > But I see this more as a DSL choice, then depending on a feature. I > personally like urls.parallel().collect most, then urls.parallel.collect > and only on last place urls.collectParallel. Frankly I would have even > expected urls.parallelCollect, if I had seen it for the first time. > > To be more specific.. assume you have > > def findOkUrls(urls) { > urls.collect { fetch(it) } > .findAll { it.status == 200 } > .groupBy { it.host } > } > findOkUrls(myUrls) > > and then you could replace that with > > def findOkUrls(urls) { > urls.collect { fetch(it) } > .findAll { it.status == 200 } > .groupBy { it.host } > } > > ParallelScope.withPool(Pool.cpu()) { > findOkUrls(myUrls) > } > > Meaning not touching the method itself. > > bye Jochen > > On 4/30/26 05:37, Paul King wrote: > > I have merged the GPars-related functionality. I'll tidy the docs but > > shout if you spot any problems. > > > > Cheers, Paul. > > > > On Mon, Apr 27, 2026 at 9:39 PM Paul King <[email protected] > > <mailto:[email protected]>> wrote: > > > > 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. > > > >
