This page looks at proposed changes to the Groovy language to support mu= ltiple assignment. Such support would allow for code such as:

=20=20

a, b =3D b, a // will transparently handle any temporary storage list =3D [1, 2, 3] def (c, d, e) =3D list assert c =3D=3D 1 && d =3D=3D 2 && e =3D=3D 3=20

It is meant to be a discussion document, not a formal definition, we wil= l work out the impact on the grammar etc. once we clarify our intention.=20

Some existing declarations involving single assigment:

=20=20

def a =3D 'a' // for examples below def b =3D 'b' // for examples below def c =3D 'c' // for examples below //------ Existing: Java style, simple case (Proposal leaves unchanged) def x1,y1,z1 =3D a // only z1 is equal to a, the others are undefined assert x1 =3D=3D null assert y1 =3D=3D null assert z1 =3D=3D a //------ Existing: Java style, list case (Proposal leaves unchanged) def x2, y2, z2 =3D [a,b,c] // only z2 is equal to [a,b,c], the others are u= ndefined assert x2 =3D=3D null assert y2 =3D=3D null assert z2 =3D=3D [a,b,c] //------- Existing: Java style, multiple equals case (Proposal leaves uncha= nged) def x3 =3D a, y3 =3D b, z3 =3D c // obvious assert x6 =3D=3D a assert y3 =3D=3D b assert z3 =3D=3D c //------- Existing: Java Java style, multiple lists case (Proposal leaves u= nchanged) def x4 =3D [a,b], y4 =3D [b,c], z4 =3D [a,c] // obvious assert x4 =3D=3D [a,b] assert y4 =3D=3D [b,c] assert z4 =3D=3D [a,c]=20

In order to not conflict with existing definitions, any variable declara=
tion which wishes to make use of multiple assignments must surround the '*unpack*' the list into the tuple of variable=
s.

=20

def (x5,y5,z5) =3D [a,b,c] // common type goes before round brackets def (x6,y6) =3D [a,b], z6 =3D c // x,y,z/5,6,7 must all be undefined def list =3D [b, c] def x7 =3D a, (y7, z7) =3D list // list can be explicit or implicit assert x5 =3D=3D a && x6 =3D=3D a && x7 =3D=3D a assert y5 =3D=3D b && y6 =3D=3D b && y7 =3D=3D b assert z5 =3D=3D c && z6 =3D=3D c && z7 =3D=3D c=20

Nested variations are also supported (this may be deferred initially):=20

=20

def ((x8,y8),z8) =3D [[a,b],c] assert x8 =3D=3D a && y8 =3D=3D b && z8 =3D=3D c=20

Differing sized tuples and lists are catered for by ignoring extra list = members and leaving any untargeted variables in the tuple set to null:

= =20=20

def (x9,y9,z9) =3D [a,b] assert x9 =3D=3D a && y9 =3D=3D b && z9 =3D=3D null def (x10,y10,z10) =3D [a,b,c,d] // d unused assert x10 =3D=3D a && y10 =3D=3D b && z10 =3D=3D c=20

There can be at most one [at the end like varargs?] 'tuple spread' opera= tor which takes the rest of the list:

=20=20

def (first11,*rest11) =3D [a,b,c,d] assert first11 =3D=3D a assert rest11 =3D=3D [b, c, d]=20

Setting a bunch of variables to the same (or related) values is accompli= shed using normal list conventions:

=20=20

def (x12,y12,z12) =3D [a] * 3 assert [x12, y12, z12] =3D=3D [a, a, a] def (x13,y13,z13) =3D 0..2 assert [x13, y13, z13] =3D=3D [0, 1, 2] def string =3D 'abcdefghi' def (x14,y14,z14) =3D (1..3).collect{ string[0..it*2]} assert [x14, y14, z14] =3D ["abc", "abcde", "abcde= fg"]=20

The tuple list is strictly a list (apart from the nesting seen earlier) = and may not contain assignments itself:

=20=20

// INVALID def (x15 =3D c, y15, z15) =3D [a,b,c]=20

Example involving nulls:

=20=20

def (x16,y16,z16) assert x16 =3D=3D null assert y16 =3D=3D null assert z16 =3D=3D null=20

Examples involving real world use:

=20=20

def (name, address1, address2, postcode) =3D myData.tokenize("\n"= ).toList() // acts like first/head/top def (name) =3D myData.tokenize("\n").toList() // equivalent to above but 'self documenting' def (name, *ignored) =3D myData.tokenize("\n").toList()=20

The left hand side (LHS) of an expression involving the simple assignmen=
t operator, `=3D`

, can be replaced with a tuple. In this case, t=
he variables will be predefined or binding variables and the result of the =
RHS of the expression will be a (possibly coerced) list which will be unpac=
ked into the tuple.

=20

def list =3D [a, b] def x17, y17 (x17, y17) =3D list assert [x17, y17] =3D=3D [a, b]=20

Where it is not ambiguous, the round brackets can be removed from the tu= ple list when used in such expressions, so the above could be written as:=20

=20

x17, y17 =3D list // LHS turned into (x17, y17)=20

If the right hand side after any assignment operator contains a comma se= parated list of subexpressions, they will be assumed to make up a list (i.e= . automatically turned into a list), so the above could be written as:

= =20=20

x17, y17 =3D a, b // RHS turned into [a, b]=20

[Note: the above probably requires lots of pouring over the grammar!]=20

If you find you need a tuple with different typed items inside, use an e= xpression form rather than the variable declaration form which requires the= types to be the same, e.g.:

=20=20

def myMonth(){ [2000, "Jan", 1] } String month int day, year (year, month, day) =3D myMonth()=20

Unlike with declarations, the LHS doesn't need to simply be a variable:<= /p>=20

=20

def map =3D [a:1, b:2] (map.a, map.b) =3D [3, 4] assert map =3D=3D [a:3, b:4] a =3D 10..15 i =3D 3 i, a[i] =3D i+1, 20 assert i =3D=3D 4 && a[3] =3D=3D 20 && a[4] =3D=3D 14=20=20

Other examples which need to work:

=20=20

x =3D 0 a, b, c =3D x, (x +=3D 1), (x +=3D 1) assert [a, b, c] =3D=3D [0, 1, 2] year, month, day =3D "2007-03-20".split('-')=20

These may not be implemented initially, but we attempt to define them he= re. It includes these:

=20=20

*=3D /=3D %=3D +=3D -=3D <<=3D >>=3D >>>=3D &=3D ^= =3D |=3D=20

First, a tuple on the LHS of a special assignment operator:

=20=20

a, b *=3D 3 // form list [a, b] (tuple spread operator ignored here if present) // call multiply() operator with arg 3 // unpack result into (a, b) tuple (spread operator applies to unpacking) // example def x18 =3D a, y18 =3D b x18, *y18 *=3D 3 assert x18 =3D a assert y18 =3D [b, a, b, a, b]=20

Now, an example with special treatment on the RHS:

=20=20

yourObject *=3D 1, 2 // calls the multiply() method on yourObject with the list [1, 2]=20

As an alternative to the semantics described here, we could define '`a *=3D 3; b *=3D 3`

'. =
This seems more useful but then if the semantics were to be consistent, the=
n '`a, b =3D 3`

' should mean '`a =3D 3; b =3D 3`

'.=20

No change, no grouping of method parameters is supported, i.e. the follo= wing is invalid:

=20=20

// INVALID def myMethod((a, b), c) { ... }=20