Description
Enumerable#size
is a confusing method. It looks inconspicuous, just return the size of a collection, no big deal. But it's actually pretty dangerous as its implementation count { true }
can lead to infinite recursion on open enumerables. Since this is a pretty nasty thing, we should try to avoid that.
If an enumerable is finite there's often an efficient way to determine the size. Such implementing types should provide an optimized size
implementation anyways. When there's no efficient way, they can fall back to count { true }
.
If an enumerable is infinite, determining its size is impossible. So it doesn't make sense to count a size
at all.
If you want to count elements in an (possibly) infinite enumerable type, you can always use count { true }
explicitly, but then it's obvious that it iterates.
When size
no longer works on inifinite enumerables, it can also serve as an indicator for other Enumerable
methods that might run into infinite recursion to abort early. They would still need to call size
because the pure existance of the method doesn't tell whether it returns a value. Range#size
for example raises on open ended ranges.
It's probably best to not remove Enumerable#size
entirely but change the implementation to raise at runtime. That way you can call size
on unions collapsed to Enumerable
. Making it abstract and force including types to decide would be another option. This would be up for debate.
As it turns out stdlib specs aren't much affected by this. XML::Attributes
and Range
(for non-int types) needed a count { true }
implementation and that's it. There are however non-speced types which rely on Enumerable
's default implementation.
Still I'm pretty confident that this breaking change won't be a huge disruption. Most including types already have a custom implementation (every Indexable
for example).