💎
ActiveSupportのArray#sumは七変化する
Railsエンジニアなら必ずお世話になるActiveSupportはRubyデフォルトの Array#sum
を拡張する処理が含まれている。
とあるライブラリ開発でActiveSupportの実装を追いかけた時に見つけた #sum
の不思議な挙動をまとめました
例によってQiitaに書いたやつをこっちにも貼り付けてみてます。
[1,2,3].sum
おそらくは1番標準的な使い方。
[5, 15, 10].sum
=> 3
ちなみにこの使い方はActiveSupportを使わなくても使用できる。
昔はRuby標準の Array#sum
よりActiveSupportの Array#sum
のほうが高速に動作したがある時期を持って本家に取り込まれたそうで今はどっちも同じ早さという話
['foo', 'bar'].sum
ActiveSupport無しだとエラーになる↓
pry(main)> ['foo', 'bar'].sum
TypeError: String can't be coerced into Integer
from (pry):1:in `+'
ところがActiveSupportありだとこうなる
pry(main)> ['foo', 'bar'].sum
=> "foobar"
唐突に +
が Array.join
に化けた。
そうは言ってもRubyで文字列を + すると連結できるのでこの挙動自体が理解できないわけではない。
[[1,2,3], [4,5]].sum
先の例同様にActiveSupport無しだとエラーになる
pry(main)> [[1,2,3], [4,5]].sum
TypeError: Array can't be coerced into Integer
from (pry):2:in `+'
じゃあActiveSupportありだとどうなるのか?
こうなります
pry(main)> [[1,2,3], [4,5]].sum
=> [1, 2, 3, 4, 5]
もしかしたら Array.flatten.sum
と同じ挙動になると予想された人もいたのではないでしょうか?
残念なことに結果は sum
されません。代わりに flatten
されます。なんで...?
おまけ
ArrayじゃなくHashでsumしたらどうなるだろうか?
pry(main)> {a: 10, b: 20}.sum
=> [:a, 10, :b, 20]
!?!?!?
ソースコードを見てると以下の記述を発見
# We can't use Refinements here because Refinements with Module which will be prepended
# doesn't work well https://bugs.ruby-lang.org/issues/13446
なるほどぉ...
Discussion