hamstergem / hamster

Efficient, Immutable, Thread-Safe Collection classes for Ruby
Other
1.89k stars 94 forks source link

3 different ways to do an empty collection #180

Closed krainboltgreene closed 9 years ago

krainboltgreene commented 9 years ago

Using vector as an example:

Hamster::EmptyVector
Hamster.vector
Hamster::Vector.new
Hamster::Vector.empty

Lets pick 1.

alexdowad commented 9 years ago

Hamster::Vector.new is different from the other 3 because it always initializes a new collection.

Hamster.vector was supposed to be a shortcut to make initializing a vector more concise, like Hamster.vector(1,2,3). I think this is redundant; people can define their own shortcuts if they want to. At the same time, you can get the canonical empty vector with Hamster.vector. But that doesn't communicate very well what it is doing. Hamster::Vector.empty or Hamster::EmptyVector is clearer.

Hamster::EmptyVector is needed, because we need somewhere to store the canonical empty vector. (Well, a class variable could also be used.) But it could be marked # @private and not included in the official public API.

Hamster::Vector.empty has the advantage of polymorphism; you can do something like collection.class.empty and get an empty collection of the same type as one which you already have. But we have #clear for that, and it can easily be reimplemented to avoid using empty if we want to remove it.

krainboltgreene commented 9 years ago

Is polymorphism something we care about?

Currently I'm of the opinion that our collection objects are too complex to inherit from intelligently, and that users should wrap their specialized objects around a Hamster object.

Additionally, I'm almost tempted to say that the canonical way should be Hamster::Vector::EMPTY, and that Hamster.vector() requires items.

Of course I'm also tempted to just drop Hamster.vector() in lieu of Hamster::Vector.new. Giving us two ways to create an empty vector:

Hamster::Vector::EMPTY # memoized
Hamster::Vector.new # fresh
alexdowad commented 9 years ago

Dropping Hamster.vector is a good idea.

Polymorphism could be useful, but not for inheritance. Where it comes in handy is when creating generic collection-manipulating functions which can operate on any type of collection, and return the same type which is passed in. This is fairly common in Clojure. Though, as I said, you could remove Vector.empty, and this could still be done using Vector#clear. The only difference would be that rather than being implemented using empty, #clear would have a separate implementation for each collection type.

krainboltgreene commented 9 years ago

I'll work up a PR, good talk.

alexdowad commented 9 years ago

The EmptyVector (etc.) constants are now all marked as private. So they are now implementation details, not part of the API. The shortcut methods like Hamster.vector have all been removed.

That leaves us with Vector.new and Vector.empty which both return an empty collection. I think this resolves the issue of too many redundant API methods.