Martin DeMello wrote:

>>>> h={}.populate(1,2,3) {|x| [x, "val #{x}"]}
>> => {1=>"val 1", 2=>"val 2", 3=>"val 3"}
>>
>> What do you think?
>
> I'd make one change to your way - keep Addable separate from
> Enumerable.

What exactly do you mean by Addable?  Did I overlook something?  If you
just mean method #add then of course you have to keep them separate
because every container must implement it's own version od #add.  Btw, the
name doesn't seem to be well chosen for Hash because not every element is
added, i.e. for duplicate keys the second one isn't exactly added.

> Once we've done that, it's simple to add both #populate
> to Addable (requires an Enumerable argument) and #build to Enumerable
> (regretting, once again, that #collect is already a synonym of #map).

Problem I see here is that requirements of Enumerable are extended: now
it's "your class needs to implement each" then it's "your class needs to
implement each and if you want to use build/populate your class also needs
to implement #add (or whatever it's called)".

> One nice thing is that classes implementing both Enumerable and
> Addable could then provide a "structural map" - e.g.
>
> class Hash
>   def smap
>     build({}) {|k,v| [k, yield(v)]}
>   end
> end
>
> {:x=>2, :y=>4}.smap {|i| i*i} #=> {:x => 4, :y => 16}

>> {:x=>2, :y=>4}.inject({}) {|h,(k,v)| h[k]=v*v;h}
=> {:x=>4, :y=>16}

This doesn't look too bad - and it's not restricted to building hashes.  I
think your #smap is not general enough.  I also start doubting whether
build / populate are actually good ideas...

> Or even something more complex like
>
> class Tree
>   def each
>     ...
>     yield [element, path]
>   end
>
>   def add args
>     element, path = args
>     ...
>   end
>
>   def smap
>     build(Tree.new) {|e, path| [(yield e), path }
>     # or
>     # Tree.new.populate(self) {|e, path| [(yield e), path]}
>   end
> end

Btw, I'd prefer the idiom

def smap
  build(self.class.new) ...
....
end

Kind regards

    robert