Robert Klemme <bob.news / gmx.net> wrote:
> 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.

I mean a collection of methods (like #populate) that rely on the
existence of #add in the receiver.
 
> > 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)".

Which is why I postulated a separate Addable mixin. #build would be
under Enumerable (receiver implements #each, argument implements #add),
and #populate under Addable (vice versa).

> > 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...

Not general enough how? The intention is to replicate a 'structure'
while transforming its elements. Even if build/populate aren't good
ideas, the underlying problem (that #map flattens its receiver into a
linear list) remains, and ought to be solved.

> Btw, I'd prefer the idiom
> 
> def smap
>   build(self.class.new) ...
> ...
> end

Yeah, that's nice. Can be implemented properly inside a mixin, then.
Perhaps with included hooks in both Enumerable and Addable to check if
the other one is already included, and to define smap if so.

Incidentally, there's an excellent FP paper titled "The Underappreciated
Unfold", which hurts my brain, but is well worth a read. There's a copy
available here:
http://www.itee.uq.edu.au/~ck/Papers/Program%20Transformation/folds%20etc/unfold.ps

martin