2020/09/24 今週の気になった bugs.ruby のチケット

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

[Feature #16828] Introduce find patterns

  • Ruby 3.0 ではパターンマッチに以下のようなサポートを追加する
# 前方に * を使ったパターンは 2.7 でもかける
case ["a", 1, "b", "c"]
in [*pre, String => x, String => y]
  p pre  # => ["a", 1]
  p x    # => "b"
  p y    # => "c"
end

# 後方に * を使ったパターンを 3.0 からサポートする
case ["a", 1, "b", "c", 2, "d", "e", "f", 3]
in [*pre, String => x, String => y, *post]
  p pre  # => ["a", 1]
  p x    # => "b"
  p y    # => "c"
  p post # => [2, "d", "e", "f", 3]
end
  • 普通に便利そう

[Feature #17156] Refinements per directory tree

  • Refinements を使用してクラス拡張した場合、ファイルごとに using する必要がある
  • しかし、各ファイルごとに using するのは手間
  • そこで usingsubdirs: キーワード引数を追加して using M, subdirs: true みたいにすると『サブディレクトリのファイルでも Refinements が有効になるようにする』という提案
    • これがあると erb などにも Refinements が適用できる
  • using するが手間なのはわかるがこれをやってしまうと Refinements の目的から外れてしまうので個人的には反対
    • どのファイルに何が using されているのが逆にわからなくなって通常のモンキーパッチよりもひどいことになりそう
  • やるとすれば using 以外に Refinements を有効化する手段を考えたほうがいいとは思う
    • erb なんかにも適用できる仕組みはほしい…
    • 個人的には gem 'hoge', using: true すると hoge の Refinements が全て適用されるような設定はほしいとは思う

[Bug #17181] Hash with default block breaks after transform values

  • Hash#transform_values で新しい Hash を生成する場合、 default_proc が引き継がれないというバグ報告
x = Hash.new { |h, k| h[k] = [] }
p x.default_proc
# => #<Proc:0x000056313e2c83f8 /tmp/vMBAvBj/59:1>

# transform_values で生成した Hash に default_proc が引き継がれない
y = x.transform_values {}
p y.default_proc
# => nil
  • これにより以下のようなコードが意図する動作を行わない
x = Hash.new { |h, k| h[k] = [] }
x[:a] << 1
x[:b] << 2

y = x.transform_values { |arr| arr.map(&:next) }
# error: callable object is expected (TypeError)
y[:c] << 3

[Feature #17177] Include the current file name and the line number in the output of p

  • p で値を出力する際に呼び出し位置も一緒に表示させようという提案
  • 例えば以下のように複数の pデバッグ出力する際にどの p の出力なのかわかりやすくするのがモチベーション
... # some buggy area or conditional branch
p foo
... # another buggy area or conditional branch
p foo
... # another buggy area or conditional branch
p bar
...
  • 流石に p の挙動を変えるのは互換性の問題があるので reject された
  • 個人的には位置情報が表示されてもそこまで見やすいとは思わない(結局位置情報からどの p なのか探す必要がある)のでどうせやるならもうちょいいい感じの出力のほうがいいとは思う
  • puts_debuggerer というデバッグ出力用のライブラリはあるらしい

[Bug #17178] Are we sure this is a bug, and not just the result of a different behavior of proc{|a|} proc{|a,|}, with keywords being treated like the latter (autosplatting) as a comma is used?

  • 以下のようにブロックの引数でキーワード引数を定義したときに期待する挙動ではないというバグ報告
# 配列を渡した場合 a = [1, 2] で受け取って欲しい
proc { |a, **kw| p [a, kw] }.call([1, 2])
# 期待する挙動: [[1, 2], {}]
# 実際の挙動:   [1, {}]

# こちらは期待する挙動
# 第一引数のみを受け取る
proc { |a, **kw| p [a, kw] }.call(1, 2)
# => [1, {}]
  • ブロックの引数は『配列を展開して引数を受け取る』みたいな特性もあるのでむずかしい…
p proc { |a| [a] }.call(1, 2)       # => [1]
p proc { |a,| [a] }.call(1, 2)      # => [1]
p proc { |a, b| [a, b] }.call(1, 2) # => [1, 2]

# 配列の場合は展開して受け取る
p proc { |a| [a] }.call([1, 2])       # => [[1, 2]]
p proc { |a,| [a] }.call([1, 2])      # => [1]
p proc { |a, b| [a, b] }.call([1, 2]) # => [1, 2]
  • ちなみに次のように Hash 値をキーワード引数で受け取る書き方も Ruby 3.0 からはエラーになるので注意する必要がある
users = [
  { name: "mado", age: 14 },
  { name: "homu", age: 14 },
  { name: "mami", age: 15 }
]

# 2.6 => no warning, error
# 2.7 => warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
# 3.0 => error: missing keywords: :name, :age (ArgumentError)
users.each { |name:, age:|
  p "name is #{name} age is #{age}"
}

merge, fix されたチケット