2020/09/04 今週の気になった bugs.ruby

内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。

Version number bumped to 3.0.0 from 2.8.0 (tentative)

  • 開発版の Ruby が 2.8 から 3.0 になった、めでたい
  • これでほぼほぼ 3.0 がリリースされる事が確定に

[Feature #16994] Sets: shorthand for frozen sets of symbols / strings

  • %記法で freeze された Set を記述できるように追加する提案
%ws{hello world} # => Set['hello', 'world'].freeze
%is{hello world} # => Set[:hello, :world].freeze
  • 例えば %ws{foo bar}.include?(str) みたいなコードで利用できる
    • Set のほうが高速なので便利そう

[Bug #17017] Range#max & Range#minmax incorrectly use Float end as max

  • Range#maxRange#minmax のその後
  • 元々は以下のようになるように修正された
# to_a したときと挙動を合わせたい
(1..3.1).to_a.max     # => 3
(1..3.1).to_a.minmax  # =< [1, 3]

(1..3.1).max
# Ruby 2.7 # => 3.1
# 修正後   # => 3
(1..3.1).minmax == [1, 3.1]
# Ruby 2.7 # => [1, 3.1]
# 修正後   # => [1, 3]
  • しかし、上記の対応で以下のようなコードがエラーになってしまった
p (42..Float::INFINITY).max
# Ruby 2.7 # => Infinity
# 修正後   # error: `floor': Infinity (FloatDomainError)
  • 上記の挙動だと既存の gem に影響があったため、以下のように修正された
p (42..Float::INFINITY).max
# Ruby 2.7 # => Infinity
# 修正後   # => Infinity
  • ここまでが今までの話
  • このチケットだが、最終的にはまつもとさんの意向により Revert された
  • 意図としては Range の役割としては『 Enumerable としての機能』と『両端のデータを持つオブジェクトとしての機能』の 2パターンあり、今回の #max #minmax は『両端のデータを返す』という意図があるということだった
  • なので (1..3.5).max は終端の 3.5 を返すのが意図する動作になる、ということらしい

[Feature #14394] Class.descendants

  • .ancestors はレシーバが『継承してる』クラス/モジュールの一覧を返す
  • .descendants はレシーバが『継承されている』クラス/モジュールの一覧を返す
module A
end

module B
  include A
end

module C
  include B
end

A.descendants    #=> [A, C, B]
B.descendants    #=> [B, C]
C.descendants    #=> [C]
class A
end

class B < A
end

class C < B
end

A.descendants    #=> [A, B, C]
B.descendants    #=> [B, C]
C.descendants    #=> [C]

[Feature #11927] Return value for Module#include and Module#prepend

  • Module#includeModule#prepend の戻り値を変更しようという提案
  • 現状では戻り値としてレシーバが返ってくる
module A; end
module B; end

A.include B # => A
A.ancestors # => [A, B]

A.prepend B # => A
A.ancestors # => [A, B]
  • これを次のような戻り値にしないか、という提案
# prepend after include
module A; end
module B; end
A.include B # => A/true
A.prepend B # => nil/false/exception
# include after prepend
module A; end
module B; end
A.prepend B # => A/true
A.include B # => nil/false/exception
# include/prepend after include/include at superclass
class A; end
module B; end
A.include M # => A/true
class B < A; end
B.include M # => nil/false/exception

[Feature #15573] Permit zero step in Numeric#step and Range#step

  • Numeric#stepby: 引数には 0 を渡すことができるが第二引数には 0 を渡すとエラーになる
    • Numeric#step の挙動としては 1.step(10, by: 1)1.step(10, 1) も同じ意味になる
    • Numeric#step の仕様は こちら
    • Range#step はキーワード引数を受け取らないが同様の挙動
# これは無限ループ
1.step(10, by: 0) { p "hoge" }

# これは実行時エラー
# error: `step': step can't be 0 (ArgumentError)
1.step(10, 0) { p "hoge" }

# ブロックを渡さない場合はエラーにならない
p 1.step(10, 0).take(10)
# => [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
1.step(10, 0)     # => ok
1.step(10, 0){}   # => ArgumentError: step can't be 0
(1..10).step(0)   # => ok
(1..10).step(0){} # => ArgumentError: step can't be 0
# avg_object_size = 0 の場合でもエラーにならずに動いてほしい
# その場合は lower_threshold が5個返ってくる
(lower_threshold..upper_threshold).step(avg_object_size).take(5)

[Feature #15547] deprecate iterator? [Feature #17133] Add deprecate warning Kernel#iterator?

merge, fix されたチケット