Using .each with constructors

Hi –

On Fri, 21 Jul 2006, Martin DeMello wrote:

enumerator thing is that the method names weren’t necessarily chosen
with this in mind, and don’t work very well. 3.times.map very
strongly does not communicate a 0…3 mapping to me. My first

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

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.

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 :slight_smile: 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.)

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.

David

On 7/21/06, [email protected] [email protected] wrote:

=> “1.9.0”
{|i| } nonintuitive. The plain 3.times.map {} doesn’t suggest a 0…3

and

3.times.map {}

I completely agree, we could easily monkeypatch Fixnum to deliver
3.numbers => [0, 1, 2]
or
3.numbers( :starting => 2 ) [2, 3, 4 ]
or
3.numbers( :interval => 42 ) => [0, 42, 2*42 ]
I do not like the name, but maybe someone has a better idea?

Cheers
Robert

which makes it seem like 3.times is return an array. Or something. I

me, there’s a big gap between “3.times.map” and mapping across 0,1,2.
enumerator.


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

On 7/21/06, [email protected] [email protected] wrote:

strongly does not communicate a 0…3 mapping to me. My first
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 predict that this will
seem intuitive once magic enumerators have been around a while - note
that it already reads perfectly as 3.times.collect

martin

On 7/21/06, [email protected] [email protected] wrote:

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
=> #<A:0x2b575de06ed0>
irb(main):008:0> a.map { Object.new }
=> [#Object:0x2b575de01e08, #Object:0x2b575de01db8,
Object:0x2b575de01d68]

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

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

Hi –

On Fri, 21 Jul 2006, Robert D. wrote:

I completely agree, we could easily monkeypatch Fixnum to deliver

3.numbers => [0, 1, 2]
or
3.numbers( :starting => 2 ) [2, 3, 4 ]
or
3.numbers( :interval => 42 ) => [0, 42, 2*42 ]

I’d rather not “monkeypatch” anything, but you could certainly extend
Fixnum to do that, with the usual caveats about extending core
classes.

I do not like the name, but maybe someone has a better idea?

Your example made me think of upto, and indeed I find this much closer
to being comprehensible than the ‘times’ version:

0.upto(3).map {|x| x * 10 }
=> [0, 10, 20, 30]

It makes some semantic sense to think of “0.upto(3)” returning
something that could be mapped, whereas “3.times” just doesn’t sound
right.

Perhaps this all comes down to the need for more nuance and
selectivity in deciding what each iterator returns, rather than having
them all automatically return enumerators.

David

Hi –

On Fri, 21 Jul 2006, Martin DeMello wrote:

There I agree with you - it’s handy, but unaesthetic for 3.times to
yield 0, 1, 2 rather than nil, nil, nil

Yes: I think you’ve pinpointed an underlying problem with “times”.
It’s really “times_with_index”, in a sense, but it’s just called
“times”.

That’s probably why the upto version is much clearer to me (see my
last post, in reply to Robert).

offer in its favour. It might be a decent optimisation for ‘map’ to
return self rather than an enumerator.

There’s another kind of consistency, though: consistent attention to
finding what’s exactly right for every syntactic and semantic
component of Ruby, one at a time, whether it’s just like others with
which it has something in common or not :slight_smile: I really think it would
be worth hand-crafting these return values based on what each method
actually does.

David

[email protected] wrote:

Perhaps this all comes down to the need for more nuance and
selectivity in deciding what each iterator returns, rather than having
them all automatically return enumerators.

But why does #times return the receiver in the first place? That can;t
possible be anywhere near as useful as the alternative. And indeed 1.9
changes #times to return an enumerator instead. Okay, but why all the
bother? Just return the collection. I’m not against enumerator returns
pre se, but I think it’s silly to do fancy things like enumerator when
there’s an obvious result in the first place. We rick allowing these
enumerators to become sort of a hackish crutch.

T.

Jacob F. wrote:

Do you really want that to generate a 10000 element array? Probably
not. I too can see (and desire) the utility of a returned collection,
but we lose the beauty of Fixnum#times as a replacement for the purely
incremental while loop.

Ouch. Good point. I like enumerators! :wink:

T.

On Sat, 22 Jul 2006 [email protected] wrote:

10000.times{ … }

Do you really want that to generate a 10000 element array? Probably
not. I too can see (and desire) the utility of a returned collection,
but we lose the beauty of Fixnum#times as a replacement for the purely
incremental while loop.

Ouch. Good point. I like enumerators! :wink:

i put in an RCR for Fixnum#of long ago

a, b, c, d = 4.of{ Hash.new }

i use is pesonally quite a bit.

-a

[email protected] wrote:

i put in an RCR for Fixnum#of long ago

a, b, c, d = 4.of{ Hash.new }

i use is pesonally quite a bit.

Cool. I like that. I’ll put in Facets.

Thanks,
T.

On 7/21/06, [email protected] [email protected] wrote:

But why does #times return the receiver in the first place? That can’t
possible be anywhere near as useful as the alternative… Just return
the collection.

The primary reason I can see is that Fixnum#times is frequently used
with a fairly large valued receiver. Such as in benchmarking:

10000.times{ … }

Do you really want that to generate a 10000 element array? Probably
not. I too can see (and desire) the utility of a returned collection,
but we lose the beauty of Fixnum#times as a replacement for the purely
incremental while loop.

Jacob F.