【一人 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.maxrange.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

この非互換な変更により RuboCopRails が壊れたという報告がされました。

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 はこんな感じで紆余曲折あり開発されているというのがわかるチケットでした。