【一人 bugs.ruby Advent Calendar 2020】[Bug #17017] Range#max & Range#minmax incorrectly use Float end as max【20日目】
一人 bugs.ruby Advent Calendar 2020 20日目の記事になります。
誤解しないように最初に書いておくとこのチケットによる Ruby 3.0 への影響はありません。
[Bug #17017] Range#max & Range#minmax incorrectly use Float end as max
このチケットは以下のように range.max
と range.to_a.max
で差異があり一貫性がないので対処しよう、という旨のチケットです。
# # これは期待する挙動 (1..3.1).to_a == [1, 2, 3] # to_a を経由した場合の結果 (1..3.1).to_a.max == 3 (1..3.1).to_a.minmax == [1, 3] # Range#max Range#minmax (1..3.1).max == 3.1 (1..3.1).minmax == [1, 3.1]
これは最初は以下のように range.max == range.to_a.max
となるように修正されました。
(1..3.1).max # => 3 (1..3.1).minmax # => [1, 3]
Range#max
の変更により既存のコードが壊れた
Range#max
の戻り値を変えたことにより以下のような非互換な挙動が発生していました。
NOTE: ちなみに 2.8.0 というのは当時はまだバージョニングが 3.0 ではなかった名残です。
# 2.7.1 : Infinity を返す # 2.8.0dev : error: `floor': Infinity (FloatDomainError) p (42..Float::INFINITY).max # 明示的に Float に変換すると OK p (42.to_f..Float::INFINITY).max
この非互換な変更により RuboCop や Rails が壊れたという報告がされました。
Float::INFINITY
の場合に非互換にしないようにした
先程の (42..Float::INFINITY).max
が非互換な挙動になってしまう為、非互換にならないような対応がなされました。
p (42..Float::INFINITY).max # 2.7.1 : => Infinity # 2.8.0dev : => Infinity
これにより件の非互換な変更に対する問題は対処されました。
最終的には全て Revert され変更はなくなった
さて、いろいろと紆余曲折があったこのチケットですが最終的にはまつもとさんの意向により全て Revert されました。
意図としては、
Range
の役割としては『Enumerable
としての機能』と『両端のデータを持つオブジェクトとしての機能』の 2パターンがある- 今回の
#max
#minmax
は『両端のデータを返す』というのが期待する挙動となる - なので
(1..3.5).max
は終端の3.5
を返すのは意図する動作になる
とのことでした。
長々と議論されたチケットですがこのチケットによる Ruby 3.0 へ影響はありません。
Ruby はこんな感じで紆余曲折あり開発されているというのがわかるチケットでした。