2020/12/03 今週の気になった bugs.ruby のチケット
内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。
Delete or warn deprecated (rev.3)
- 非推奨になっている機能を消す PR
- まだマージはされてないんですが Ruby 3.0 までに merge されるかも?
[Feature #17355] Using same set of names in or-patterns (pattern matching with Foo(x) | Bar(x))
# * いまは以下のようなパターンマッチ構文を書くとエラーになる
case [1, 2] # error: duplicated variable name in [1, a] | [a, 3] end
- これをエラーではなくて動作させたいというチケット
- 具体的なユースケースとしては以下のようなコードが上げられている
User = Struct.new(:email) Admin = Struct.new(:email) Moderator = Struct.new(:email) def user_email(user) # case user # in User(email:) then email # in Admin(email:) then email # in Moderator(email:) then email # end # 上のコードを以下のようにかける case user in User(email:) | Admin(email:) | Moderator(email:) then email end end pp user_email(User.new("hoge@example.com")) pp user_email(Admin.new("hoge@example.com")) pp user_email(Moderator.new("hoge@example.com"))
- これは普通に便利そうな予感
- ちなみにコメントで以下のように
&
を使った場合の例も書かれていました- https://bugs.ruby-lang.org/issues/17355#note-2
- 現状はこういうコードも動作しません
def user_email(user) case user in (User | Admin | Moderator) & { email: } then email end end
[Feature #8421] add Enumerable#find_map and Enumerable#find_all_map
Enumerable#find_map
とEnumerable#find_all_map
を追加する提案- チケット自体はかなり前に立てられたもの
- 最近『以下のように代替できるよー』ってコメントがあったのでキャッチアップできた
# .find_map{..}
ary.lazy.filter_map{..}.first
- これは便利なんだけど
#filter_map
も入ったしfind_map
ほしいなあ… - ちなみに最近以下のようなコードを見かけてしまいつらくなりました
# find で見つかったときにキーを返したい!というコードだが実際には [key, value] を返してしまう… result = hash.find { |key, value| key if hoge(value) }
- これ結構実用的なケースだと思うんですけどどうだろうなー
- もうちょっと具体化してコメントしてみてもいいかもしらん
[Bug #17354] Module#const_source_location is misleading for constants awaiting autoload
- Ruby 2.7 で
Module.const_source_location
というメソッドが追加されました - これは引数の定数の定義位置を返すメソッドです
# hoge.rb class Foo end # Foo が定義された位置を返す pp Module.const_source_location(:Foo) # => ["/home/test/hoge.rb", 2]
- このメソッドと
autoload
と組み合わせると.const_source_location
戻り値が意図しない値になるのでは?というバグチケット
# foo.rb class Foo end
# hoge.rb # まだ Foo は定義されていないので nil や false を返す pp Module.const_defined?(:Foo) # => nil pp Module.const_source_location(:Foo) # => false # autoload を定義する autoload :Foo, './foo' # autoload を定義しているときに const_defined? を呼ぶと true を返すようになる pp Module.const_defined?(:Foo) # => true # この時に .const_source_location は autoload で定義した位置を返す # 今回はこの戻り値に対してのチケットになる pp Module.const_source_location(:Foo) # => ["/home/test/hoge.rb", 7] # 実際に Foo を参照した場合は .foo.rb が読み込まれて Foo が定義される pp Module.const_get(:Foo) pp Module.const_defined?(:Foo) # => true pp Module.const_source_location(:Foo) # => ["/home/test/foo.rb", 2]
- 上記のケースの
Module.const_sjurce_location(:Foo)
の戻り値をどうするべきか、みたいな議論がされている[]
を返すようにするとか["./foo", nil]
を返すようにするとか `Module#autoload?
を使うことで何かしら対処できないかとか
autoload
周り無限にきびしそう…
[Feature #17353] Functional chaining operator
func(obj)
をパイプライン演算子を使ってobj |> func
とかけるようにする提案
puts(format("the number is %d", make_stuff(gets.to_i))) # を gets.to_i |> make_stuff |> format "the number is %d" |> puts # と書く
- ぶっちゃけこっちの方のパイプライン演算子はそこまでほしいと思わないんですがどうなんですかねえ
- Ruby だと
func(obj)
というよりもobjl.func
と書くことの方が多いので
- Ruby だと
- 以下のように
#then
を使って書くようなコード例がコメントに書いてあった
gets.to_i .then(&method(:make_stuff)) .then { format "the number is %d", _1 } .then(&method(:puts))
- 乱雑で読みやすいコードとはいえないけどこっちのほうが Ruby っぽくて好きではある
[Bug #17348] Shadowed method can not be evaluated on the line that it is shadowed
- 以下のようなコードを実行するとエラーになるよー、というバグ報告
def a "" end # error: undefined method `[]' for nil:NilClass (NoMethodError) a = a[0] puts a.inspect
- これは
a = a[0]
の行で先にa = nil
という変数が定義され、その変数a
に対して[0]
でアクセスしようとしているからですね - このあたりは Ruby に精通してないと意図しない挙動に見えるので難しい…
[Feature #17342] Hash#fetch_set
- 次のように
Hash#fetch
を呼び出しつつ、自身に値を代入したい事がある
homu = { name: "homu" } # fetch で見つからなかった場合に任意の値を割り当てる homu.fetch(:age) { homu[:age] = 14 } pp homu # => {:name=>"homu", :age=>14}
- これを行う
Hash#fetch_set
を追加しようという提案
homu = { name: "homu" } # fetch で見つからなかった場合にそのキーの要素に割り当てる homu.fetch_set(:age) { 14 } pp homu # => {:name=>"homu", :age=>14}
- 名前をどうするのかーみたいな議論をされている
- 雑に
#fetch!
とかでいいような気もする
- 雑に
- 実用的なケースで言うと以下みたいな感じ?
# foo.hoge.first が2回呼ばれてしまいつらい hash.fetch(foo.hoge.first) { hash[foo.hoge.first] = default } # こっちだと foo.hoge.first が1回しか出てこない hash.fetch_set(foo.hoge.first) { default }
- これを読んでてこのチケットを思い出した