On Aug 27, 9:32=A0am, Brian Candler <B.Cand... / pobox.com> wrote:

> But that breaks a lot of useful stuff. e.g.
>
> =A0 =A0a =3D File.open("/etc/passwd") { |f| f.map { |line| line.split(':'=
) } }

Right. My implementation is just a quick hack. And you make a good
point, this requires what Smalltalk calls 'species'. Dumb name IMO,
but anyhow it could be done in Ruby simple with an extra method or
class method (not sure which is best). So something like...

    def IO.enumerator ; Array ; end

    def map(&block)
      o =3D self.class.enumerator.new
      each{ |e|  o << yield(e) }
      o
    end

> or
>
> =A0 =A0str =3D File.read("/etc/passwd")
> =A0 =A0a =3D str.map { |line| line.split(':') }

Not this case. String is no longer Enumerable.

Hmm... I wonder if the same should be so for IO now too. Eg.

  a =3D File.open("/etc/passwd") { |f| f.each_line.map { |line|
line.split(':') } }

> That is: the contract for map is to run through an Enumerable and build t=
he
> results into an Array. It is not intended to run through an Enumerable an=
d
> to append the results into a new instance of whatever class that object
> originally was.
>
> It may not even be possible to create a new instance of that class, if th=
e
> initialize method requires arguments.
>
> Aside: I suppose you could have such a pattern if you explicitly provided
> the object to append to.
>
> =A0 module Enumerable
> =A0 =A0 def into(target=3D[], &blk)
> =A0 =A0 =A0 blk ||=3D lambda { |e| e }
> =A0 =A0 =A0 each { |e| target << blk[e] }
> =A0 =A0 =A0 target
> =A0 =A0 end
> =A0 end
>
> =A0 src =3D "one\ntwo\nthree\n"
> =A0 p src.into([])
> =A0 p src.into("") { |e| "*#{e}" }
> =A0 src.into($stdout) { |e| e.upcase } =A0 # process a line at a time
>
> =A0 data =3D ["one\n", "two\n", "three\n"]
> =A0 p data.into("")
>
> Perhaps even map itself could take the thing to write 'into' as an argume=
nt.

That's an interesting idea too actually. If map could take argument to
override the enumerator.

  def map(o=3Dnil)
    o ||=3D self.class.enumerator
    ...

Also, I point out that this addresses #select (and others?), which I
believe have now been overridden in Hash to return a Hash.

> You could also argue that Hash#update should accept any Enumerable as its
> argument, so you could write
>
> =A0 a =3D [[1,:one], [2,:two], [3,:three]]
> =A0 h =3D {}.update(a)
>
> to convert an associative array to a hash.

  #to_h ?

> But I've never needed either construct. Probably these things belong in t=
he
> Facets library, if not there already.

Facets has #mash as a hash equivalent of #map. However, it's rarely
used simply because when we weigh having a dependency vs. just doing
it, you end up just doing it.... You say you don't have a need for it,
but I suspect you've do what we all do:

  c.inject({}){ |h,e| ...

or the equivalent

  h =3D {}; c.each{ |e| h ...  }

> There is value in minimising the
> amount of magic in the core language, and there's a lot there already.

Not at all! I see it as removing "magic". Having to override methods
and add new ones to get desired behaviors --that's "magic". When
simple modifications to shared traits provides the same behaviors in
more concise and powerful ways, how can that not be good?

T.

PS. I finally found the ruby-talk thread! Ruby-Talk#256299, but I've
pretty much summed it up here.