From: Martin DeMello Date: 2006-07-21T22:18:59+09:00 Subject: Re: opacity of magic enumerator (was: Re: Using .each with constructors) On 7/21/06, dblack@wobblini.net wrote: > On Fri, 21 Jul 2006, Martin DeMello wrote: > > > > If you think about it, though, the underlying problem is that 3.times > > passes a parameter into the block, which is what makes 3.times.map > > {|i| } nonintuitive. The plain 3.times.map {} doesn't suggest a 0...3 > > mapping, but rather an enumerator consisting of three "passes" which > > map naturally turns into a 3-object array. > > I'm not getting the "naturally" part :-) I mean, of course it's all > non-natural in a sense, but I just can't seem to get my brain to > perceive 3.times as returning something that responds to map this way. The way I see it, the notion underlying a "magic enumerator" is that it returns an object whose 'each' method would perform the same sequence of yields that the method being magic_enumerated does. If 3.times did a { yield; yield; yield }, mapping over its magic enumerator would produce the "natural" result irb(main):001:0> class A irb(main):002:1> include Enumerable irb(main):003:1> def each irb(main):004:2> yield; yield; yield irb(main):005:2> end irb(main):006:1> end => nil irb(main):007:0> a = A.new => # irb(main):008:0> a.map { Object.new } => [#, #, ] > Maybe part of the problem is that it suggests an equivalence between: > > [0,1,2].map {} > > and > > 3.times.map {} > > which makes it seem like 3.times is return an array. Or something. I > don't even know exactly; it just reads very badly to me. There I agree with you - it's handy, but unaesthetic for 3.times to yield 0, 1, 2 rather than nil, nil, nil > > I predict that this *will* seem intuitive once magic enumerators > > have been around a while - note that it already reads perfectly as > > 3.times.collect > > I think that if something has to be around for a while to be > comprehensible, it loses the "intuitive" badge :-) I'm sure that > eventually the process of mentally translating it will get faster.... > I guess I'm just used to Ruby semantics not having big gaps, and for > me, there's a big gap between "3.times.map" and mapping across 0,1,2. > (I'm not sure about your .collect point; to me that suffers from > exactly the same problem.) If I could go back in time and persuade Matz to do things my way, "map" would be structure-preserving, and "collect" would gather its results in an array. That's the way the two names read to me, at any rate. (Then, again, I'd also rename Fixnum#times Fixnum#each and mix in Enumerable, in which community opinion seems to be very firmly against me :)) > I'm also still troubled by the rather wide net cast by magic > enumerators. I don't think anyone came up with an answer to my > earlier question: When would you ever need this: > > some_enumerable.map.other_method > > ? If the answer is "never", then I don't think map should return an > enumerator. That is indeed a good point, and 'consistency' is the only thing I can offer in its favour. It might be a decent optimisation for 'map' to return self rather than an enumerator. martin