You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@groovy.apache.org by Paul King <pa...@asert.com.au> on 2026/04/27 11:39:15 UTC

features for groovy-6.0.0-alpha-1

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.

Re: features for groovy-6.0.0-alpha-1

Posted by Paul King <pa...@asert.com.au>.
Hi mg,

Yes, literal lists and literal maps are existing functionality backed by
"isCase".
Which essentially boils down to contains for a list and containsKey for a
map, e.g.:

assert switch('foo') {
  case [foo: 'bar'] -> 'found'
} == 'found'

In the above I could replace 'bar' with any variable, e.g. h, t, x, y,
*items.
So, if we don't want to break backwards compatibility, we need something
different.
Using var or def isn't valid syntax now, so allows us to turn on "pattern
matching mode" instead of "isCase mode". I did look at alternatives where
we didn't need those two modes but all had problems. But that GEP targets
Groovy 7, so we have time to keep thinking.

Cheers, Paul.


On Fri, May 1, 2026 at 3:14 AM MG <mg...@arscreat.com> wrote:

> 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.
>
>
>

Re: features for groovy-6.0.0-alpha-1

Posted by MG <mg...@arscreat.com>.
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.

Re: features for groovy-6.0.0-alpha-1

Posted by Paul King <pa...@asert.com.au>.
Hi mg,

It should probably just say "It" instead of "Path A". I had a much longer
example and asked AI to help summarise it.

So here, when you use `Files.lines(path).withCloseable { src -> ... }` and
destructure like `def (header, *body) = src`, the `body` variable is still
a lazy Stream. If that stream is consumed, the underlying file handle is
closed correctly.

Or another way, it avoids bringing the whole file into memory just to
process (or not) the body.

Cheers, Paul.

On Wed, Apr 29, 2026 at 2:06 AM MG <mg...@arscreat.com> wrote:

> Hi Paul,
>
> what is "Path A keeps the Stream lazy and propagates onClose -- the killer
> demo for the streaming contract." supposed to mean ?
>
> Cheers,
> mg
>
> 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.
>
>
>

Re: features for groovy-6.0.0-alpha-1

Posted by MG <mg...@arscreat.com>.
Hi Paul,

what is "Path A keeps the Stream lazy and propagates onClose -- the 
killer demo for the streaming contract." supposed to mean ?

Cheers,
mg

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.

Re: features for groovy-6.0.0-alpha-1

Posted by Paul King <pa...@asert.com.au>.
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 <bl...@gmx.org> 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 <paulk@asert.com.au
> > <ma...@asert.com.au>> 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.
> >
>
>

Re: features for groovy-6.0.0-alpha-1

Posted by MG <mg...@arscreat.com>.
Hi Jochen,

 1. @GPars features: I had just started writing a similar response,
    along the line of having an annotation that could "auto parallel"
    code...
     1. General question: Is there a specific reason that we do not do
        auto-parallelization in general, similar in spirit to e.g.
        Java's BigDecimal implementation that heuristically picks
        different algorithms for multiplication under the hood, based on
        the size of the numbers in play ?
 2. @collectParallel:
     1. I prefer urls.parallel over urls.parallel(), due to it being
        less verbose & parallel not changing anything in urls or being
        expensive, but just being a tag or marker that influences
        behavior in the call chain.
     2. parallel(urls).collect would imho be even better, but since it
        would need to be a static method it is alas inconvenient with
        regards to Intellisense/autocompletion.
     3. Also fo Intellisense reasons I think collectParallel is overall
        better than parallelCollect.

Cheers,
mg

Am 30.04.2026 um 17:36 schrieb Jochen Theodorou:
> 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 <paulk@asert.com.au 
>> <ma...@asert.com.au>> 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.
>>
>

Re: features for groovy-6.0.0-alpha-1

Posted by Jochen Theodorou <bl...@gmx.org>.
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 <paulk@asert.com.au 
> <ma...@asert.com.au>> 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.
> 


Re: features for groovy-6.0.0-alpha-1

Posted by Paul King <pa...@asert.com.au>.
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 <pa...@asert.com.au> 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.
>