5.collect vs. [0, 1, 2, 3, 4].collect

I have always thought of ie. 5.collect{ } as a syntactic shortcut for [0, 1, 2, 3, 4].collect{ }, but I just noticed that they are not the same, since 5.collect does not have a second argument:

[0, 1, 2, 3, 4].collect{|n, i| n.debug(\n); i.debug(\i) } // n == i
5.collect{|n, i| n.debug(\n); i.debug(\i) }  // i == nil

I am wondering about the reasoning behind this design choice. Wouldn’t it be more consistent if 5.collect had a second argument which always is equal to the first argument? Does this have to do with optimization?

My personal opinion is that collect should be only for collections – SimpleNumber:collect doesn’t need to exist, and shouldn’t exist. Array.fill(5, { ... }) would be preferable usage.

I know that’s not a popular opinion but I think it’s justifiable. I’m also aware that 5.collect is justifiable on other grounds, but I still don’t like it.

hjh

2 Likes

Float does not have .collect, so Integer has .collect, but SimpleNumber does not.

The second argument of (0..4).collect is the index of the element of an array. Integer has no index. So I think the current design makes a lot of sense if we think only in terms of AnInteger.collect.

However, there is an inconsistency when we extend the scope to .do:

  • 5.0.do has two arguments, and the second argument is the same as the first argument.
  • 5.do has two arguments, and the second argument is the same as the first argument.

Examples:

  • 5.0.do
    5.0.do { |i, j| [i, j].postln }
    

    [0.0, 0.0]
    [1.0, 1.0]
    [2.0, 2.0]
    [3.0, 3.0]
    [4.0, 4.0]
    → 5.0

  • 5.do
    5.do { |i, j| [i, j].postln }
    

    [0, 0]
    [1, 1]
    [2, 2]
    [3, 3]
    [4, 4]
    → 5

  • 5.0.collect
    5.0.collect { |i, j| [i,j] }
    

    ERROR: Message ‘collect’ not understood.

  • 5.collect
    5.collect { |i, j| [i,j] }
    

    → [[0, nil], [1, nil], [2, nil], [3, nil], [4, nil]]

I think:

  • AFloat.collect should be supported as AFloat.do is, otherwise AFloat.do should not be supported as AFloat.collect is not.
  • The second argument of SimpleNumber.do and SimpleNumber.collect should be integers indicating the index of execution from 0. The fact that the class of the second argument of .do is different according to the class of the receiver of .do is very confusing.

I wanted to say this a long time ago, but I could not because I thought I was missing something. However, after reading the opinions of @Thor_Madsen and @jamshark70, it might not be a bad idea to suggest this!

Thanks for sharing your thoughts!

1 Like

Thanks for showing the different .do and .collect examples. I did not know you could do Float.do and it seems a little strange that it is allowed, also because:

5.4.do{ |i, j| [i, j].postln }

[ 0.0, 0.0 ]
[ 1.0, 1.0 ]
[ 2.0, 2.0 ]
[ 3.0, 3.0 ]
[ 4.0, 4.0 ]
-> 5.4

The second argument of SimpleNumber.do and SimpleNumber.collect should be integers indicating the index of execution from 0

Yes for SimpleNumber.do. I don’t necessarily see the need to extend this behavior to .collect, it seems like extending one questionable design choice to another method just for consistency. It could potentially, maybe not very likely, be a breaking change for some existing .do code (I think)? I can’t really think of a credible example, but there must be cases where the class of the index matters…

I definitely see @jamshark70’s point that SimpleNumber.collect (and AnInteger.collect) should not have been allowed from the beginning, even though I will still use the method, just cause I am so used to it by now and it is short and sweet.

2 Likes