2021/12/16 今回の気になった bugs.ruby のチケット

今週は {Method,UnboundMethod}#{public?,private?,protected?} が追加されました。

[Bug #18405] Regression in Struct member setter method parameters

S = Struct.new(:foo)
S.instance_method(:foo=).parameters
# 最適化前 => [[:req, :_]]
# 最適化後 => [[:req]]
  • セッターメソッドなのに引数を受け取らないのはおかしいんじゃないか?との事
    • 確かに
  • これは既に修正済み

[Bug #18408] Rightward assignment into instance variable

  • 右代入でインスタンス変数に代入できないのは意図しているのか?というバグ報告
# error: syntax error, unexpected instance variable
42 => @v
  • 右代入はパターンマッチの機能を使っているんですがパターンマッチがインスタンス変数などを束縛できないからですね
# error: syntax error, unexpected instance variable
case 42
in @v
end

[Bug #18396] An unexpected "hash value omission" syntax error when without parentheses call expr follows

  • 先週話していた Hash の省略記法で『次の行の式』が値になってしまうというバグ報告の続き
key = "key"
# p(key:) ではなくて p(key: 42) になる
p key:
42
# => {:key=>42}
validate :something, if: -> {condition }

# コード長いとここで改行している可能性がある
validate :something, if:
                     -> {condition }

[Feature #18384] Pattern Match Object

  • パターンマッチを保持するオブジェクトを定義する提案
  • 例えば次のように pattern でパターンマッチを保持し、ブロック引数に渡すような感じ?
list_of_people.select(&pattern(
  first_name: /^F/,
  last_name: /r$/,
  age: 20..40
))
  • ProcRegexp と同じようなイメージ
TARGET_PERSON = PatternMatch.new(first_name: 'something')
list_of_people.select(&TARGET_PERSON)
  • 同じようなパターンマッチを DRY 的に使い回す場合には便利そう?
  • パターンマッチの場合は変数に束縛する機能もあるのでそこをどうするのがいいんだろうか

[Feature #11689] Add methods allow us to get visibility from Method and UnboundMethod object.

class Object
  def debugging(name)
    original = instance_method(name)

    # 提案
    # visibility が `:public` `:protected` `:private` を返す
    method_visibility = original.visibility
    # 既存の実装だとこう書く必要がある
    # method_visibility = if private_method_defined?(name)
    #                       :private
    #                     elsif protected_method_defined?(name)
    #                       :protected
    #                     else
    #                       :public
    #                     end
    define_method(name) { |*args, &block|
      pp name
      original.bind(self).call(*args, &block)
    }

    # 元のメソッドに合わせる
    send(method_visibility, name)
  end
end

class X
  def hoge
    pp 1 + 2
  end
  private :hoge

  debugging :hoge
end

# error: private method `hoge' called for #<X:0x000055f0b1abf3e0> (NoMethodError)
X.new.hoge
  • 元々の提案の #visibility は入らなかったが {Method,UnboundMethod}#{public?,private?,protected?} のように判定するメソッドが追加された
class Object
  def debugging(name)
    original = instance_method(name)

    # 既存の実装だとこう書く必要がある
    method_visibility = if original.private?
                          :private
                        elsif original.protected?
                          :protected
                        else
                          :public
                        end
    define_method(name) { |*args, &block|
      pp name
      original.bind(self).call(*args, &block)
    }

    # 元のメソッドに合わせる
    send(method_visibility, name)
  end
end

class X
  def hoge
    pp 1 + 2
  end
  private :hoge

  debugging :hoge
end

# error: private method `hoge' called for #<X:0x000055f0b1abf3e0> (NoMethodError)
X.new.hoge
  • どちらかと判定メソッドが必要なケースが多そうなのでこっちのほうが汎用性は高そうですね

[Feature #18402] Argument Labels

  • 次のようにキーワード引数に予約語を使用することはできるが参照する事はむずかしい
def change_color(to:, for:, until:)
  new_color, user, end_date = to, for, until

  do_something_with(to)
  do_something_else_with(for, until)  # What does this do with which data again?
end

change_color(to: :blue, for: user, until: DateTime.tomorrow)
  • これを対応するために以下のようにキーワード引数とは別に参照する名前を定義できるようにする提案
def change_color(to new_color:, for user:, until end_date:)

  do_something_with(new_color)
  do_something_else_with(user, end_date) # No use of reserved keywords anymore, and readable variable name!
end

change_color(to: :blue, for: user, until: DateTime.tomorrow)
def change_color(to: => new_color, for: @current_user => user, until: DateTime.tomorrow => end_date)
  • ちなみに以下のように Hash の省略記法を使うとスムーズにアクセスができる
def change_color(to:, for:, until:)
  do_something_with({to:}[:to])
  do_something_else_with({for:}[:for], {until:}[:until])  # What does this do with which data again?
end

change_color(to: :blue, for: user, until: DateTime.tomorrow)

[Feature #18410] Proposal to make inspect include underscores on numerics

  • 964218442 のような数値を表示する際に 964_218_442 みたいな表示にする提案
  • これは便利そうな気がしつつ非互換になってしまうのがちょっと気になる…
  • あと単に数値として表示したい場合もあるので必ずしも _ を付けたいとは限らないんじゃないかなあ
  • ロケールによって _ も変える必要があるとコメントされている