I imagine this has come up before, though I can't find anything about it. I
was talking to a friend last night who mentioned that in Smalltalk, the
#collect and #select methods (and some others) return the same type as the
object they're called on, and in Ruby they always return an Array, no matter
what the receiver type is. This seems like a good idea to me, though there
are some questions:
I'm not sure what you mean is a good idea, the Smalltalk way, the Ruby
way, or that there's a difference?
Actually, it's not completely true that in Smalltalk collect and
select return the same class (and in both Ruby and Smalltalk classes
are not types), as the receiver. In Smalltalk they return an instance
of the species of the receiver. The abstract Collection class
implements collect and select which calls self species to get the
class to instantiate the result.
The collect method then enumerates the receiver and adds each element
to the result using the add: method. Select pretty much is the same
except that adds the element conditional on what the block evaluation
returns.
The abstract implementation of species is just self class, but it is
overridden when that's not appropriate. For example the Interval class
(ST's rough equivalent of Range) overrides species to return Array.
I'm going to reorder your questions a bit.
2. Is part of the contract of #map that you get back a collection of the
same size as the input? If so, what happens if you #map a Set and produce
duplicates? The output will be smaller than the input (similarly for hash
key collisions). Certainly from an FP viewpoint, a map is a one-to-one
transform from input to output values, so it would seem logical to have as
many output values as input values.
Not in Smalltalk. If you send collect to a Set, you'll get back a Set
of the results of evaluating the block for each element of the
receiver, which means that duplicates will be removed. So the result
Set may be smaller than the original.
1. What do they mean when applied to a Hash? Do you want a list of values,
or keys, or pairs? Or do you want to map each key and value to a different
key and value, and have #map build a Hash for you? If so, how do you
represent that as a return value? Is it [key, value] or {key => value} ?
3. There is no uniform method for adding items to a collection. We have
Array#<<, Hash#=, Set#add and Set#add?. For linked lists, adding to the
collection would involve changing pointers on existing members of the
collection. Is it possible to come up with a single method/message whose
role is to provide a uniform way to add something to any collection?
Smalltalk implements Dictionary (which is the rough equivalent of
Ruby's Hash) as a subclass of Set. A Smalltalk Dictionary is a Set of
Associations. An instance of Association represents a key-value pair,
and two Associations are considered equal if the keys are equal. When
you enumerate a Dictionary in Smalltalk with the do: method (the rough
equivalent of #each), the values of the associations are enumerated.
There's also an associationsDo: method which enumerates the
associations.
In Smalltalk there is a uniform method to add an item to a collection
add: So to add an item to a Dictionary you add an association.
So what about collect/select for Dictionaries.
In Smalltalk, Dictionary overrides collect: to return an instance of
Bag holding the values in the Dictionary, a Bag is just what it sounds
like, it's a bag of objects which is unordered and may have
duplicates.
Dictionary also overrides select: to use associationsDo: instead of
do: it returns an instance of the species of the receiver (a
Dictionary for Dictionary instances, but maybe something else for a
subclass) after adding each association value from evaluating the
block argument for each association in the receiver. So in this case
you'd get back a Dictionary or something like a Dictionary.
All of this is based on "Blue-Book" Smalltalk-80, there might be some
slight variation in more modern Smalltalk implementations/dialects.
Now, is the Ruby or Smalltalk approach 'better' I guess if I had my
druthers, I guess I differ a bit from my friend David A. Black here,
and I'd prefer it if Ruby were a bit more Smalltalk like here, and
Ruby 1.9 has made some steps in that direction.
But it is what it is.
···
On Fri, Aug 28, 2009 at 5:42 AM, James Coglan<jcoglan@googlemail.com> wrote:
--
Rick DeNatale
Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale