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 <[email protected]> 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.
>
>
>

Reply via email to