Closed LisiLisenok closed 8 years ago
@LisiLisenok that is the nature of iterables created with { ... }
. They are lazy and their elements will be re-evaluated each time they are iterated. As your code demonstrates, using [ ... ]
is the right approach if you do not want the lazy behavior.
(I assume you meant value iter = { for ( i in 0 : 2 ) counterIter.call() };
)
@jvasileff I agree. But even with lazy evaluation it may be performed just once. This helps with mutability at least.
In any case it would be good to mention this behavior and this difference between {...}
and [...]
in the documentation.
Thank you for clarification. Lets consider this is not a bug)
Which documentation were you looking at which didn't mention this? On 14 Jan 2016 7:34 pm, "Lisi" notifications@github.com wrote:
@jvasileff https://github.com/jvasileff I agree. But even with lazy evaluation it may be performed just once. This helps with mutability at least. In any case it would be good to mention this behavior and this difference between {...} and [...] in the documentation.
Thank you for clarification. Lets consider this is not a bug)
— Reply to this email directly or view it on GitHub https://github.com/ceylon/ceylon/issues/5913#issuecomment-171753850.
The Streams, sequences and tuples section of the language tour don't mention it. It's only mentioned, in passing, in the Comprehensions section. Many of the features announced in blog posts should probably be highlighted in the tour, as first time users will go there first. At any rate, only people who write frameworks read language specs :smile:
@fwgreen thanks. I am one who has never read language specs. Hope some day...
@tombentley Tom, looking in language tour and language module API I thought that {...}
is like a sugar for Iterable or language internal type which exactly corresponds to Iterable interface specification. But that's not completely true. After my own experiments and John clarification I understand that comprehesions are some separated types with their own specifications. And these types are not described in API documentation.
I would propose to add here and here a short paragraph describing comprehesions evaluation and other specific implementations of Iterable interface
Agreed it should be clarified in the tour. Something like "comprehensions are lazy and not memoized (so iterating a comprehension will cause each element to be reevaluated every time its needed)", or something like that.
Not quite so sure about the Iterable
doc itself, since really that's about the iterable contract itself, and comprehensions are just one implementation of that. But I guess it could fit after the third paragraph if we can find the right words.
A Pull request would be a great help!
comprehensions are lazy and not memoized
That's not special for comprehensions, because that behaviour is also in effect for any Iterable
literal, no? { f() }
will also always call f()
on every iteration, no?
Yeah, so I suppose we also need something on http://ceylon-lang.org/documentation/1.2/tour/sequences/. But I think saying it again (reiterating it, haha) on the comprehensions page is not a bad thing.
@FroMage [...]
satisfies Iterable
but evaluates just once - when initialized, Array
, ArrayList
and so on - all satisfied Iterable
but evaluates just once. As I understand only {...}
calls f()
on every iteration, or not?
Yes, all {}
Iterable
literals are lazy and non-memoised (always (re)evaluate on iteration), and all []
Sequential
literals are eager and memoised.
The fact that Sequential
satisfies Iterable
does not imply that it makes iterating them lazy.
I have added documentation to the tour. Closing.
Consider counter - each calling increases a total count by 1. And comprehesion of results of the counting (at least as expected). But this comprehesion recalculates its items each time when evaluated. Hope the code below is more clear... Problems:
At the same time sequence [] shows different results (works as expected)
class CounterCall() { shared variable Integer count = 0;
}
shared void testIterable() { CounterCall counterIter = CounterCall(); value iter = [ for ( i in 0 : 2 ) counterIter.call() ]; print ( "total calls
counterIter.count
array:iter
" ); print ( "total callscounterIter.count
array:iter
" ); print ( "total callscounterIter.count
array:iter
" );}
output: total calls 0 array: { 0, 1 } total calls 2 array: { 2, 3 } total calls 4 array: { 4, 5 } total calls 2 array: [0, 1] total calls 2 array: [0, 1] total calls 2 array: [0, 1]