Skip to content

Remove iteration in Enumerable#size #10014

Open
@straight-shoota

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).

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions