From: Rick DeNatale Date: 2009-08-28T22:35:22+09:00 Subject: Re: #map, #select semantics On Fri, Aug 28, 2009 at 5:42 AM, James Coglan wrote: > 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. -- 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