[ruby-core:66627] [ruby-trunk - misc #10560] confusion between x=x+y, x+=y, x.concat(y) and y.each{|z| x<<z}

From: chris@...
Date: 2014-12-01 19:03:50 UTC
List: ruby-core #66627
Issue #10560 has been updated by Chris Seaton.


I disagree with making the proposed change to +=. I would find it extremely surprising for += to modify an existing Array object. I really can't imagine any mental model of Ruby where it would make intuitive sense to do that. It goes against existing Ruby semantics and would have be taught as a special case. It will likely break existing Ruby code. It increases the complexity of Ruby semantics and Ruby implementations. It introduces implicit mutation, which is probably something we want less of, not more.

However I do agree that we need better documentation for things like +=. I'm not sure where I would look for documentation of something like that. We don't really have language documentation, do we?

I would also be in favour of transparently implementing Array#+ as something similar concat where through escape analysis it can be determined that the original object is never needed again, but that is a lot of ask of Ruby implementations.

----------------------------------------
misc #10560: confusion between x=x+y, x+=y, x.concat(y) and y.each{|z| x<<z}
https://bugs.ruby-lang.org/issues/10560#change-50242

* Author: Michal Papis
* Status: Assigned
* Priority: Normal
* Assignee: Zachary Scott
* Category: doc
* Target version: current: 2.2.0
----------------------------------------
while discussing a ticket I have noticed that there is no documentation for `+=`

I was expecting `+=` to behave as `concat` but instead it behaves as `x=x+y` which for every operation clones the array and updates the variable with new value so it behaves similarly to `x=x.dup.concat(y)` and is slightly faster, but using plane `x.concat(y)` is a lot faster from both `each<<` and `+=`

I would either like to get:

 - updated docs that describe concept of `+=` and show the difference from `concat`
 - or change `+=` to use `concat` which is faster - and add docs ;) (I would expect `+=` to use `concat` when available)

here is a test:

    require 'benchmark'
    
    rep = 10_000
    
    Benchmark.bmbm do |x|
      {
        1..25 => [],
        "a".."z" => "",
      }.each do |base, storage|
    
        base = base.to_a
        basej = base
        class_name = storage.class.to_s
    
        x.report(class_name+'#concat') do
          a = storage.dup
          basej = base.join if storage == ""
          rep.times { a.concat(basej) }
        end
    
        x.report(class_name+'#<<') do
          a = storage.dup
          basej = base.join if storage == ""
          rep.times { base.each { |e| a << e } }
        end
    
        x.report(class_name+'#+=') do
          a = storage.dup
          basej = base.join if storage == ""
          rep.times { a += basej }
        end
    
        x.report(class_name+'#dup.concat') do
          a = storage.dup
          basej = base.join if storage == ""
          rep.times { a = a.dup.concat(basej) }
        end
    
      end
    end


and here are results on my machine:

                            user     system      total        real
    Array#concat        0.000000   0.000000   0.000000 (  0.001422)
    Array#<<            0.020000   0.000000   0.020000 (  0.014356)
    Array#+=            1.270000   0.230000   1.500000 (  1.498558)
    Array#dup.concat    2.720000   0.190000   2.910000 (  2.915701)
    String#concat       0.000000   0.000000   0.000000 (  0.001072)
    String#<<           0.030000   0.000000   0.030000 (  0.025828)
    String#+=           0.130000   0.010000   0.140000 (  0.135143)
    String#dup.concat   0.210000   0.020000   0.230000 (  0.227470)




-- 
https://bugs.ruby-lang.org/

In This Thread

Prev Next