Hi Tobias and Eliot,
Folks there is NO BASIS at the level of Collection for assuming that do: [ : each | ^ false ] is faster than ^self size=0. In fact, the proper assumption is the opposite -- that #size is the optimized method, not #do:.
I don't understand on what _either_ performance-assumption is grounded.
Exactly. The original basis for making this change is groundless.
self size = 0 doesn't work for infinite collections.
What's an infinite collection, and why wouldn't self size = 0 work for its #isEmpty? I assume it will need to override #size, so if it answers Float infinity then #isEmpty is false. What's not working? And, irregardless, custom collections like that define what THEY need to work properly (e.g., #size, #isEmpty, whatever) in their own class, not going changing 30-year old base library code to make themselves work..
The suggested implementation of isEmpty does indeed require a comment (I didn't implement it) but it does work for infinite collections. The suggested implementation os isEmpty is also faster than elf size = 0 for any collection of sufficient size.
For example:
| b n | b := Bag someInstance. n := 100000. { b size. [1 to: n do: [:ign| b isEmptyOId]] timeToRun. [1 to: n do: [:ign| b isEmpty]] timeToRun } #(20402 3293 21)
and the cut off is quite low:
| n | n := 10000. ((1 to: 10) collect: [:iguana| (1 to: SmallInteger maxVal) detect: [:i| b := (1 to: i) asBag. [1 to: n do: [:ign| b isEmptyOId]] timeToRun > [1 to: n do: [:ign| b isEmpty]] timeToRun]]) average asFloat 4.9
So for collections that are 5 elements or more the "clever" implementation is faster.
Then add Bag>>#isEmpty, don't change Collection>>#isEmpty, because it changes a 30-year old assumption about how #isEmpty works for every other kind of Collection.