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