[ 
https://issues.apache.org/jira/browse/GROOVY-8561?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Paul King updated GROOVY-8561:
------------------------------
    Description: 
Groovy currently "promotes" a singleton instance of an object into an array for 
assignments, e.g.:
{code:java}
Integer[] nums = 42
assert nums instanceof Integer[]
assert nums.size() == 1
assert nums[0] instanceof Integer
{code}
This aligns with how Groovy behaves if you try to call `.each{}` on a 
non-aggregate. It treats it like a singleton collection and "iterates" over the 
one item.

The existing behavior also currently works for singleton Closures:
{code:java}
Closure[] fns0 = { }
assert fns0 instanceof Closure[]
assert fns0.size() == 1
assert fns0[0] instanceof Closure
{code}
To add support for Java array notation, we will need to partially disable this 
behavior. The proposed change involves smart parsing, e.g. it will distinguish 
cases which must be an array and cases which must be a closure but there are 
some degenerate edge cases which will become breaking changes.

The case with the empty closure above will no longer work, instead you will get 
this behavior, i.e. an empty array is given precedence over an empty closure:
{code:java}
Closure[] fns1 = { }
assert fns1 instanceof Closure[]
assert fns1.size() == 0
{code}
To get the old behavior back you have a couple of options. Firstly, you can 
provide the explicit closure argument delimiter:
{code:java}
Closure[] fns2 = { -> } // can't be an array
assert fns2 instanceof Closure[]
assert fns2.size() == 1
assert fns2[0] instanceof Closure
{code}
Or don't rely on singleton promotion and explicitly provide also the array 
curly braces:
{code:java}
Closure[] fns3 = { { } }
assert fns3 instanceof Closure[]
assert fns3.size() == 1
assert fns3[0] instanceof Closure
{code}
Similarly, for the case of the identity closure:
{code:java}
Closure[] fns4 = { it }
{code}
Previously this worked but under this proposal will give:
{noformat}
groovy.lang.MissingPropertyException: No such property: it ...
{noformat}
Your options are to add the extra array braces as per above, or use explicit 
params, e.g.:
{code:java}
Closure[] fns5 = { it -> it }
assert fns5 instanceof Closure[]
assert fns5.size() == 1
assert fns5[0] instanceof Closure
{code}
Alternatively, for this special case you have the following additional option:
{code:java}
Closure[] fns6 = Closure.IDENTITY
assert fns6 instanceof Closure[]
assert fns6.size() == 1
assert fns6[0] instanceof Closure
{code}
There are other cases as well, e.g. this code which currently creates a closure 
array containing a closure returning the integer 0:
{code:java}
Closure[] fns7 = { 0 }
{code}
will no longer be supported and will fail with:
{noformat}
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast 
object '0' with class 'java.lang.Integer' to class 'groovy.lang.Closure'
{noformat}
The solutions are similar to previously (explicit delimiter):
{code:java}
Closure[] fns8 = { -> 0 }
{code}
or (explicit outer array braces):
{code:java}
Closure[] fns9 = { { 0 } }
{code}

Here is the PR: [https://github.com/apache/groovy/pull/691]

  was:
Groovy currently "promotes" a singleton instance of an object into an array for 
assignments, e.g.:
{code:java}
Integer[] nums = 42
assert nums instanceof Integer[]
assert nums.size() == 1
assert nums[0] instanceof Integer
{code}
This aligns with how Groovy behaves if you try to call `.each{}` on a 
non-aggregate. It treats it like a singleton collection and "iterates" over the 
one item.

The existing behavior also currently works for singleton Closures:
{code:java}
Closure[] fns0 = { }
assert fns0 instanceof Closure[]
assert fns0.size() == 1
assert fns0[0] instanceof Closure
{code}
To add support for Java array notation, we will need to partially disable this 
behavior. The proposed change involves smart parsing, e.g. it will distinguish 
cases which must be an array and cases which must be a closure but there are 
some degenerate edge cases which will become breaking changes.

The case with the empty closure above will no longer work, instead you will get 
this behavior, i.e. an empty array is given precedence over an empty closure:
{code:java}
Closure[] fns1 = { }
assert fns1 instanceof Closure[]
assert fns1.size() == 0
{code}
To get the old behavior back you have a couple of options. Firstly, you can 
provide the explicit closure argument delimiter:
{code:java}
Closure[] fns2 = { -> } // can't be an array
assert fns2 instanceof Closure[]
assert fns2.size() == 1
assert fns2[0] instanceof Closure
{code}
Or don't rely on singleton promotion and explicitly provide also the array 
curly braces:
{code:java}
Closure[] fns3 = { { } }
assert fns3 instanceof Closure[]
assert fns3.size() == 1
assert fns3[0] instanceof Closure
{code}
Similarly, for the case of the identity closure:
{code:java}
Closure[] fns4 = { it }
{code}
Previously this worked but under this proposal will give:
{noformat}
groovy.lang.MissingPropertyException: No such property: it ...
{noformat}
Your options are to add the extra array braces as per above, or use explicit 
params, e.g.:
{code:java}
Closure[] fns5 = { it -> it }
assert fns5 instanceof Closure[]
assert fns5.size() == 1
assert fns5[0] instanceof Closure
{code}
Alternatively, for this special case you have the following additional option:
{code:java}
Closure[] fns6 = Closure.IDENTITY
assert fns6 instanceof Closure[]
assert fns6.size() == 1
assert fns6[0] instanceof Closure
{code}
There are other cases as well, e.g. this code which currently creates a closure 
returning the integer 0:
{code:java}
Closure[] fns7 = { 0 }
{code}
which will now fail with:
{noformat}
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast 
object '0' with class 'java.lang.Integer' to class 'groovy.lang.Closure'
{noformat}
The solutions are similar to previously (explicit delimiter):
{code:java}
Closure[] fns8 = { -> 0 }
{code}
 
 or (explicit outer array braces):
{code:java}
Closure[] fns9 = { { 0 } }
{code}
Here is the PR: [https://github.com/apache/groovy/pull/691]


> Support java-like array
> -----------------------
>
>                 Key: GROOVY-8561
>                 URL: https://issues.apache.org/jira/browse/GROOVY-8561
>             Project: Groovy
>          Issue Type: New Feature
>            Reporter: Daniel Sun
>            Assignee: Daniel Sun
>            Priority: Major
>
> Groovy currently "promotes" a singleton instance of an object into an array 
> for assignments, e.g.:
> {code:java}
> Integer[] nums = 42
> assert nums instanceof Integer[]
> assert nums.size() == 1
> assert nums[0] instanceof Integer
> {code}
> This aligns with how Groovy behaves if you try to call `.each{}` on a 
> non-aggregate. It treats it like a singleton collection and "iterates" over 
> the one item.
> The existing behavior also currently works for singleton Closures:
> {code:java}
> Closure[] fns0 = { }
> assert fns0 instanceof Closure[]
> assert fns0.size() == 1
> assert fns0[0] instanceof Closure
> {code}
> To add support for Java array notation, we will need to partially disable 
> this behavior. The proposed change involves smart parsing, e.g. it will 
> distinguish cases which must be an array and cases which must be a closure 
> but there are some degenerate edge cases which will become breaking changes.
> The case with the empty closure above will no longer work, instead you will 
> get this behavior, i.e. an empty array is given precedence over an empty 
> closure:
> {code:java}
> Closure[] fns1 = { }
> assert fns1 instanceof Closure[]
> assert fns1.size() == 0
> {code}
> To get the old behavior back you have a couple of options. Firstly, you can 
> provide the explicit closure argument delimiter:
> {code:java}
> Closure[] fns2 = { -> } // can't be an array
> assert fns2 instanceof Closure[]
> assert fns2.size() == 1
> assert fns2[0] instanceof Closure
> {code}
> Or don't rely on singleton promotion and explicitly provide also the array 
> curly braces:
> {code:java}
> Closure[] fns3 = { { } }
> assert fns3 instanceof Closure[]
> assert fns3.size() == 1
> assert fns3[0] instanceof Closure
> {code}
> Similarly, for the case of the identity closure:
> {code:java}
> Closure[] fns4 = { it }
> {code}
> Previously this worked but under this proposal will give:
> {noformat}
> groovy.lang.MissingPropertyException: No such property: it ...
> {noformat}
> Your options are to add the extra array braces as per above, or use explicit 
> params, e.g.:
> {code:java}
> Closure[] fns5 = { it -> it }
> assert fns5 instanceof Closure[]
> assert fns5.size() == 1
> assert fns5[0] instanceof Closure
> {code}
> Alternatively, for this special case you have the following additional option:
> {code:java}
> Closure[] fns6 = Closure.IDENTITY
> assert fns6 instanceof Closure[]
> assert fns6.size() == 1
> assert fns6[0] instanceof Closure
> {code}
> There are other cases as well, e.g. this code which currently creates a 
> closure array containing a closure returning the integer 0:
> {code:java}
> Closure[] fns7 = { 0 }
> {code}
> will no longer be supported and will fail with:
> {noformat}
> org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast 
> object '0' with class 'java.lang.Integer' to class 'groovy.lang.Closure'
> {noformat}
> The solutions are similar to previously (explicit delimiter):
> {code:java}
> Closure[] fns8 = { -> 0 }
> {code}
> or (explicit outer array braces):
> {code:java}
> Closure[] fns9 = { { 0 } }
> {code}
> Here is the PR: [https://github.com/apache/groovy/pull/691]



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

Reply via email to