On Jun 21, 2007, at 1:31 PM, Todd Benson wrote:
> On 6/21/07, Alexander Presber <aljoscha / weisshuhn.de> wrote:
>> ...
>> Making Enumerable behave more agnostic to the class it is mixed in
>> (by letting the class itself provide a method to add an "element" to
>> an instance of itself, Enumerable becomes truly mixable into anything
>> that provides "each" and "<<".
>> Then doing reject on any class that mixes in Enumerable will yield a
>> filtered instance of that class, not Array.
>>
>> That said - I think there should be no such thing as TomsEnum or any
>> special implementation.
>> Enumerable is the place to define methods for all things containing
>> enumerable elements.
>>
>> >>
>> >> The other option would require an #each_assoc method (maybe assoc
>> >> isn't the best term, but anyhow...)
>> >>
>> >>   module Enumerable
>> >>     def select_assoc(&blk)
>> >>       h = {}
>> >>       each_assoc{|k,v| h[k]=v if blk[k,v]}
>> >>       h
>> >>     end
>> >>   end
>> >>
>> >> The downside here of course, is twice the number of Enumerable
>> >> methods.
>> > And although I cannot imagine a case, how do we know that there are
>> > not Enumerables that take three or fourtytwo params ;).
>> > After all Enumerable is a Mixin and we have to be prepared that  
>> it be
>> > mixed in, right?
>>
>> Yes, one could impossibly provide for all possible Mixees like this.
>>
>> Yours,
>> Alex
>
> A duck is a bird.  It doesn't behave exactly like every other bird you
> know about.  But you can be relatively certain it has wings.  I
> suspect the least common denominator return of the object is there for
> several reasons, including testing, ease of the ruby language
> development, etc.  Why the difference for the Hash returning a
> different object between select and reject?  I think it's one of those
> oversight things.
>
> ...
>
> Todd

What no one seems to have (directly) focussed on is that Hash#each  
gives two-element [k,v] arrays back as the content.  Is this what  
Alexander Presber means with:

>> Enumerable is the place to define methods for all things containing
>> enumerable elements.

It's how you define the elements that are enumerable.  Hash#each_key,  
Hash#each_value, and Hash#each_pair make this explicit.  Hash#each  
being simply Hash#each_pair (the documentation says it's the other  
way around, but if they're synonyms what does it matter) makes the  
definition of a "pair" or more explicitly "key-value pair" a good  
place to focus.

Perhaps if a Hash was a collection of "entries" so #each returned  
something closer to {k=>v} rather than an array.

s = [1, 2, 3]
r = s.class.new
s.each {|e| r << e}
r
=> [1, 2, 3]

Currently, if we replace the value of the array s with a hash { 1 =>  
'uno', 2 => 'dos', 3 => 'tres' }
you'd need something like:

   class Hash
     alias_method :<<, :update
     alias_method :orig_each, :each
     def each
       orig_each {|k,v| yield({k=>v}) }
     end
   end

to get a similar result:

s={1=>'uno',2=>'dos',3=>'tres'}
r = s.class.new
s.each {|e| r << e}
r
=> {1=>"uno", 2=>"dos", 3=>"tres"}

if the notation to "unpack" block arguments .each {|(k,v)| ... }  
would pick apart a hash entry like {k=>v} the same way that it would  
[k,v] (which, of course, doesn't actually need the parentheses), then  
the "syntactic compatibility" would be close enough for my brain,  
eyes, and fingers.  I think that the arity of block that each_pair  
expects should remain 2 and each_pair should yield([k,v]) (so the  
meaning of "pair" for English-speakers is maintained -- I realize I  
being anglophilic here).  The arity of the block expected by #each  
would be 1.

It seems to me that you'd also get "benefits" like.

{1=>"uno", 2=>"dos", 3=>"tres"}.sort_by {|k,v| v}
=> [{2=>"dos"}, {3=>"tres"}, {1=>"uno"}]

Since you'd clearly need to transform to a type that was ordered if  
you wanted to "sort" an unordered Hash.

And if there were a #<=> defined for a Hash entry, you could just use  
#sort.

This is clearly a slippery slope because you'd then have to redefine  
Hash#shift to give {k=>v} rather than [k,v] also.  You'd almost  
certainly want something like #first and #last to give the key and  
value from {k=>v} like they'd pull those parts from [k,v].

[Blah!  I took too long to answer and Alex(ander) got another  
response to Todd in with some of the same ideas, but I decided to  
post anyway.]

-Rob

Rob Biedenharn		http://agileconsultingllc.com
Rob / AgileConsultingLLC.com