Details
-
Type:
Improvement
-
Status:
Closed
-
Priority:
Major
-
Resolution: Won't Fix
-
Affects Version/s: 1.0-beta-5
-
Fix Version/s: 1.0-beta-6
-
Component/s: None
-
Labels:None
-
Number of attachments :
Description
1> [1,2,3].each .inject(0) { i, f | println "$
{i}$
{f}"; ++i }
2> go
0 1
1 2
2 3
1> import java.io.File
2> new File("test.txt").eachLine.inject(0) { i, line | println "$
$
{line}"; ++i }
3> go
No signature of method org.codehaus.groovy.runtime.MethodClosure.inject() is applicable for argument types: (java.lang.Integer, CommandLine8$1) values: [0, CommandLine8$1@5e179a]
The following from John Wilson:
[1,2,3].each creates a closure over the call of each on [1, 2, 3]. This is expected behaviour and is like myObject.myMethod producing a closure over the call of myMethod on myObject.
So:
a = [1,2,3].each
a
prints
1
2
3
Now the closure generated isn't your common or garden closure, it implements groovy.lang.Range
we can see that by running
for (i in a)
{println i}prints
1
2
3
Now groovy.lang.Range extends java.util.List so the closure is a Collection!
Because the closure is a collection you can call inject on it.
Now the interesting question is how does the system know to make the closure implement Range for each and can we make it do so for eachLine, eachByte, eachFile?
I'm afraid I don't know the answer to this. there's nothing I can see in DefaultGroovyMethods which would tell the compiler to do this. It may well be wired in behaviour.
The fact that this works for each looks to be an unintended consequence of wanting to make the closure a Range (which is useful elsewhere - e.g. in for loops).
I'm reopening this as a clone, because although there is a "workaround" by declaring a variable and just using the normal iteration as in:
int i = 0
new File("foo.txt").eachLine {
i++; println "current line is $i";
}
The problem is that this approach requires exposing a variable that has no other purpose than the scope of the iteration. I'm not saying that the original poster's suggestion of ".eachLine.inject" is necessarily the best approach, however there should be some mechanism to perform inject to avoid having dangling variables. For example:
new File("foo.txt").eachLineInject(0) { i, line ->
i++; println "current line is $i"
}
The same applies for the collect, find, eachWithIndex, etc helper methods. And it applies not only to File, but also to Reader and other similar situations, where it would be nice to be able to use these methods in a streaming mode.