2022/04/21 今回の気になった bugs.ruby のチケット
今週はレシーバに対して Kernel#p
が呼び出せるようにする提案がありました。
[Feature #18736] self-p for method chain
- 次のようにメソッドチェーンの間にレシーバを出力したいという要望チケット
class Object def sp(method=nil, *args, &block) if method Kernel.p self.public_send(method, *args, &block) elsif block_given? Kernel.p block.call(self) else Kernel.p self end return self end end # sp のレシーバを標準出力する p [1,2,3].map{|x| x**2}.sp.map{|x| x**2} # output: # [1, 4, 9] # [1, 16, 81] # ブロックの結果を出力する [1,2,3].sp{|x| "my List = #{x}"} # output: # "my List = [1, 2, 3]" # レシーバに対して sum(-10) を呼び出した結果を出力する [1,2,3].sp(:sum,-10) # output: # -4
#tap
すると以下のように書くことができる
# sp のレシーバを標準出力する p [1,2,3].map{|x| x**2}.tap(&method(:p)).map{|x| x**2} # output: # [1, 4, 9] # [1, 16, 81] # ブロックの結果を出力する [1,2,3].tap{|x| p "my List = #{x}"} # output: # "my List = [1, 2, 3]" # レシーバに対して sum(-10) を呼び出した結果を出力する [1,2,3].tap { p _1.sum(-10) } # output: # -4
- 別チケットだと以下のようなのが提案されている
- これ系のメソッドはめっちゃほしいんだよなあ…
[Bug #18729] Method#owner and UnboundMethod#owner are incorrect after using Module#public/protected/private
- 次のように
public / protected / private
を呼び出した後に#owner
が変わってないというバグ報告
class A protected def foo :A end end class B < A p [instance_method(:foo), instance_method(:foo).owner, instance_methods(false), A.instance_methods(false)] public :foo p [instance_method(:foo), instance_method(:foo).owner, instance_methods(false), A.instance_methods(false)] end # outptu: # [#<UnboundMethod: B(A)#foo() owner.rb:2>, A, [], [:foo]] # [#<UnboundMethod: B(A)#foo() owner.rb:2>, A, [:foo], [:foo]]
B
でfoo
が再定義されているのにowner
がB(A)#foo()
のままになっている- 期待する挙動は以下の通り
[#<UnboundMethod: B(A)#foo() owner.rb:2>, A, [], [:foo]] [#<UnboundMethod: B#foo() owner.rb:2>, B, [:foo], [:foo]]
[Bug #18741] Slicing an Array using index out of range inconsistent return type
- 範囲外の値を
Array#[]
に渡したときの挙動が一貫していないというバグ報告
def test_slicing_arrays array = [:peanut, :butter, :and, :jelly] assert_equal [:peanut], array[0,1] assert_equal [:peanut, :butter], array[0,2] assert_equal [:and, :jelly], array[2,2] assert_equal [:and, :jelly], array[2,20] assert_equal [], array[4,0] # 範囲外を指定した時にこっちは [] を返す assert_equal [], array[4,100] assert_equal nil, array[5,0] # 同様に範囲外を指定しているがこっちは nil を返す end
- これは [Feature #16822] Array slicing: nils and edge cases と似たようなチケットでこちらは Reject されている
[Feature #18742] Introduce a way to tell if a method invokes the super
keyword
- メソッド内で
super
を呼び出しているかどうかを判定するメソッドを追加する提案
class X def a end; p instance_method(:a).calls_super? #=> false def b super end; p instance_method(:b).calls_super? #=> true def c super if false end; p instance_method(:c).calls_super? #=> true def d eval 'super' end; p instance_method(:d).calls_super? #=> false (I doubt there's a reasonable way for this to return true) end
- https://bugs.ruby-lang.org/issues/18618#note-1 の実装をする時に利用できると書いてある
- ユースケースがかなりエッジケースだけど他に利用できる箇所とかあるかなあ
2022/04/14 今回の気になった bugs.ruby のチケット
今週は Kernel#then
に引数を渡せるようにする提案がありました。
[Feature #18690] Allow Kernel#then
to take arguments
Kernel#then
に引数を追加する提案- 通常はレシーバをブロックの引数として受け取るが
1.5.then{|x| Math.atan(x)}
#then
に渡した引数をブロックの引数でも受け取るようにする提案
3.then(4){|x, y| Math.hypot(x, y)}
- チケットでは
honyarara.then{|x| foo(x) bar(fugafugafuga) baz(hogehogehoge) qux(x, fugafugafuga, hogehogehoge) }
- の場合に
honyarara.then(fugafugafuga, hogehogehoge){|x, y, z| foo(x) bar(y) baz(x) qux(x, y, z) }
- とかけるユースケースが提示されている
then
の中で複数回同じメソッドを呼ぶ場合だと便利そうですかね?
obj.bar.then(hoge.foo) { |bar, foo| foo.piyo(foo, bar, foo) }
[Bug #18688] when array's default value is empty hash adding a hash key value changes all array elements
- 以下のように空の Hash の配列の要素を書き換えると全て書き変わってしまうというバグ報告
# {} が3つある配列を生成する ah = Array.new(3, {}) p ah # => [{}, {}, {}] # 1つの要素を書き換える ah[1][:foo] = 'bar' # この時に配列の要素は以下のようになる p a # 期待する挙動 => [{}, {:foo=>"bar"}, {}] # 実際の挙動 => [{:foo=>"bar"}, {:foo=>"bar"}, {:foo=>"bar"}]
- これは配列の要素の参照先が全て同じになっているので意図する挙動になる
# これは参照しているオブジェクトがバラバラ a = [{}, {}, {}] p a[0].__id__ # => 60 p a[1].__id__ # => 80 p a[2].__id__ # => 100 # Array.new の場合は全て同じオブジェクトを参照している a = Array.new(3, {}) p a[0].__id__ # => 120 p a[1].__id__ # => 120 p a[2].__id__ # => 120 # n 個の同じ要素の配列を定義したい場合はブロックを渡して定義するのが安全 a = Array.new(3) { {} } p a[0].__id__ # => 60 p a[1].__id__ # => 80 p a[2].__id__ # => 100
[Feature #18685] Enumerator.product: Cartesian product of enumerables
- 要素ごとの全ての組み合わせの
Enumerator
を生成するEnumerator.product
を追加する提案
product = Enumerator.product(1..3, ["A", "B"]) p product.class #=> Enumerator product.each do |i, c| puts "#{i}-#{c}" end =begin output 1-A 1-B 2-A 2-B 3-A 3-B =end
Array#product
はあるんですがそれとは別にEnumerable
でも使いたいって感じですかね
# Enumerable に対しては使えないので Array に変換する必要がある pp (1..3).to_a.product(["A", "B"]) # => [[1, "A"], [1, "B"], [2, "A"], [2, "B"], [3, "A"], [3, "B"]]
[Feature #18618] no clobber def
- 以下のようにスーパークラスのメソッドがある場合は再定義しないようなコードがある
class Dog def bark 'bark!' end end class Poodle < Dog # 既に同名のメソッドが定義されていれば例外にする raise if method_defined? :bark def bark 'bow-wow' end end
- これを次のように
ncdef
でかけるようにしたいという提案
class Dog def bark 'bark!' end end class Poodle < Dog ncdef bark # "no clobber" def 'bow-wow' end end # => #<MethodAlreadyDefined: Method `bark' already defined.>
ApplicationRecord
とかのサブクラスを定義したい時にこういうことをしたいらしい- 意図しないメソッドの上書きってどれぐらい弊害があるんですかね…
- これがあると全部
ncdef
で定義する必要がありそう
- これがあると全部
- 他の言語だと逆に『スーパークラス側でメソッドが上書きできないこと』を明示化するような機能は存在する
- C++ の例だと以下のような感じ
// 基底クラス class base { // final が付いていると派生クラスで再定義できない virtual void func_final() final; }; // 派生クラス class sub : base { // error: error: declaration of 'func_final' overrides a 'final' function virtual void func_final() final; };
- sorbet だと
final
をサポートしているらしい
2022/04/09 今回の気になった bugs.ruby のチケット
今週は MatchData#[]
のバグ報告などがありました。
[Bug #18670] MatchData#[start, length] pads nil values when negative start is given
MatchData#[]
にマイナス値を渡すとnil
で埋めされた配列を返すバグ報告
# [-1, 5] は最後の値から5文字分を返す # String#[] や Array#[] は結果的に最後の値だけを返す pp "hello"[-1, 5] # => "o" pp [1, 2, 3][-1, 5] # => "3" # MatchData#[] の場合は足りない値が nil 埋めされた配列を返す result = /.*/.match("aaaa") pp result # => #<MatchData "aaaa"> pp result[-1, 5] # => ["aaaa", nil, nil, nil, nil]
- このバグは修正済み
# MatchData#[] の場合は足りない値が nil 埋めされた配列を返す result = /.*/.match("aaaa") pp result[-1, 5] # Ruby 3.1 => ["aaaa", nil, nil, nil, nil] # Ruby 3.2 => ["aaaa"]
[Feature #18683] Allow to create hashes with a specific capacity.
Hash
オブジェクトを生成する時にオブジェクトのサイズを指定できるようにする提案- 更に
rb_hash_new_capa(long)
も拡張する必要がある
- 更に
hash = Hash.new(capacity: 1000)
String
やArray
には同様の機能があるのでHash
にも欲しいという意図らしい
String.new(capacity: XXX) Array.new(XX) / rb_ary_new_capa(long)
- 以下の箇所にメリットがあるとの事
[Bug #18677] BigDecimal#power (**) returns FloatDomainError when passing an infinite parameter
BigDecimal#power (**)
に無限大を渡すと意図しないエラーになるというバグ報告
require "bigdecimal" # error: `**': Computation results in 'Infinity' (FloatDomainError) BigDecimal(10) ** BigDecimal("Infinity")
- サンプル実装も書かれているんですが意図としては
BigDecimal::INFINITY
を返してほしい感じですかね?
require "bigdecimal/util" class BigDecimal < Numeric def **(other) if other.infinite? == 1 if self > 1 BigDecimal::INFINITY elsif self == 1 self elsif self >= 0 BigDecimal(0) else power(other) end else power(other) end end end def puts_and_eval(string) puts string p eval(string) end puts_and_eval "10 ** BigDecimal::INFINITY" # => Infinity puts_and_eval "1 ** BigDecimal::INFINITY" # => 0.1e1 puts_and_eval "0.1 ** BigDecimal::INFINITY" # => 0.0 puts_and_eval "0 ** BigDecimal::INFINITY" # => 0.0 puts_and_eval "-1 ** BigDecimal::INFINITY" # => -0.1e1
[Bug #18673] Anonymous block forwarding fails when combined with keyword arguments
- 以下のようにキーワード引数が定義されている時に匿名のブロック引数を別のメソッドに渡すとエラーになるバグ報告
def inner "yielded #{yield}" end def block_only(&) # OK inner(&) end def pos_arg(arg1, &) # OK inner(&) end def kwarg(arg1, kwarg1:, &) # NG inner(&) end def kwarg_with_default(arg1, kwarg1: "kwarg_default", &) # NG inner(&) end
- このバグは修正済み
[Bug #18415] String#rpartition is not sufficiently greedy compared to String#partition
- 以下のような貪欲な正規表現を
String#rpartition
に渡すと意図する値が返ってこないバグ報告
# [最初のセパレータより前の部分, セパレータ, それ以降の部分] を返す pp "...123...".partition /\d+/ # => ["...", "123", "..."] # [最後のセパレータより前の部分, セパレータ, それ以降の部分] を返すが意図した値ではない pp "...123...".rpartition /\d+/ # => ["...12", "3", "..."]
- これは意図する挙動らしくてドキュメントにその意図を追記する対応がされている
- ちなみに以下のような正規表現だと
123
がセパレータになる
pp "...123...".rpartition(/(?<!\d)\d+/) # => ["...", "123", "..."]
Ruby 3.2.0 Preview1 が出た
Ruby 3.2.0 Preview1 が出ました。
RUby 3.1.0 Preview1 が去年の11月に出たことを考えると Ruby 3.2 の Preview1 は出るのが早かったですね?
Ruby 3.2.0 Preview1 の新機能としては WASIベースのWebAssemblyサポート
と Regexp timeout
になりますね。
気になる人は試してみるとよいと思います。
余談1: git protocol が廃止された
rbenv で Ruby 3.2.0 Preview1 をインストールするために ruby-build を更新しようとするとエラーが出た。
$ git pull fatal: リモートエラー: The unauthenticated git protocol on port 9418 is no longer supported. Please see https://github.blog/2021-09-01-improving-git-protocol-security-github/ for more information.
これは暗号化されてない git protocol が廃止されたためらしい。
雑に remote
先を変えて対応。
$ git remote -v origin git://github.com/sstephenson/ruby-build.git (fetch) origin git://github.com/sstephenson/ruby-build.git (push) $ git remote set-url origin https://github.com/rbenv/ruby-build.git $ git remote -v origin https://github.com/rbenv/ruby-build.git (fetch) origin https://github.com/rbenv/ruby-build.git (push)
余談2:Ruby 3.2.0 から libyaml
が同梱されなくなった
数日前から rbenv install 3.2.0-dev
すると require': cannot load such file -- digest (LoadError)
になってビルドに失敗していました。
これ、結構困っていたんですが原因は Ruby 3.2.0 から libyaml
が同梱されなくなったからでした。
なので sudo apt install libyaml-dev
する事で解決しました。
これは Ruby 3.2.0 Preview1 のリリースノートに書いてあったから気づけたんですが、読まなかった一生わからなかったなあ。
同様に Ruby 3.2.0 Preview2 から libffi
が同梱されなくなるらしいので注意する必要があります。
Ruby で新しい Unicode 規格にバージョンアップする時になにを行っているのかまとめてみた
Ruby は定期的に Unicode の新しい規格に対応しているんですがその時になにが行われているのかが気になったので調べてみました。
この記事では『Ruby の Unicode 14.0.0 対応がなにを行っているか』を追っていきたいと思います。
ちなみに Unicode 14.0.0 は次の Ruby 3.2 でサポートされる予定です。
Ruby の Unicode 14.0.0 対応
Ruby の Unicode 14.0.0 対応のチケットは以下になります。
上のチケットの作業コミットはコメントに書かれているんですが以下の通りです。
commit 48f1e8c5d85043e6adb8e93c94532daa201d42e9 Author: Martin Dürst <duerst@it.aoyama.ac.jp> Date: Tue Mar 15 18:55:16 2022 +0900 Fix version check to use Emoji version for emoji-variation-sequences.txt commit 56d9d78f14b73cb9f609558e6b760dde50872fb6 Author: Martin Dürst <duerst@it.aoyama.ac.jp> Date: Tue Mar 15 17:17:15 2022 +0900 Remove Unicode 13.0.0 related files commit 267f0089d3255c1f06ab5adf9f6c77b1ccfd2771 Author: Martin Dürst <duerst@it.aoyama.ac.jp> Date: Mon Mar 14 08:37:53 2022 +0900 clarify meaning of version guards for Unicode version specs [ci skip] commit 1b571d0abf6070673320b11a30769bbe74d12e39 Author: Benoit Daloze <eregontp@gmail.com> Date: Sun Mar 13 13:18:56 2022 +0100 Fix guards for unicode versions specs commit 45187a0fcddecc74dacc1881f2405a5ebe198081 Author: Martin Dürst <duerst@it.aoyama.ac.jp> Date: Sun Mar 13 10:52:24 2022 +0900 comment out failing Unicode/Emoji version checks temporarily commit 8f59482f5d1f77542fe3c8e8434cb1dbbc764ecf Author: Martin Dürst <duerst@it.aoyama.ac.jp> Date: Sat Mar 12 21:33:51 2022 +0900 add some tests for Unicode Version 14.0.0 commit 9b545b0caf2ccc89718ba02ff631d2a68b96a831 Author: Martin Dürst <duerst@it.aoyama.ac.jp> Date: Fri Mar 11 17:18:42 2022 +0900 update specs to check for Unicode Version 14.0.0/Emoji Version 14.0 commit 2672502457523317268ac24704cf85df91e2cae6 Author: Martin Dürst <duerst@it.aoyama.ac.jp> Date: Fri Mar 11 17:11:32 2022 +0900 mention Unicode Version 14.0.0 commit 8e1f3a96aecb3defc34556d75e3d2a0867416082 Author: Martin Dürst <duerst@it.aoyama.ac.jp> Date: Fri Mar 11 16:48:26 2022 +0900 switch UNICODE_BETA back to NO commit 45e0711f29f9ce65cd34ad14e3af1546ccc7252e Author: Martin Dürst <duerst@it.aoyama.ac.jp> Date: Thu Dec 9 16:41:09 2021 +0900 update Unicode Version to 14.0.0 and Emoji version to 14.0
と、いうことでまずはこのコミットの内容を下から時系列に1つ1つ見ていこうかな、と思います。
[45e0711f29] update Unicode Version to 14.0.0 and Emoji version to 14.0
common.mk
の UNICODE_VERSION
と UNICODE_EMOJI_VERSION
のバージョンが 14
に更新されています。
UNICODE_EMOJI_VERSION
が別にあるんですね?
また作業当時はまだ beta 版だったので UNICODE_BETA = YES
となっているぽい?
他には enc/unicode/14.0.0/casefold.h
と enc/unicode/14.0.0/name2ctype.h
の2つのファイルが新しく追加されて lib/unicode_normalize/tables.rb
が更新されています。
このあたりのファイルはあとで詳しくみていきましょうかね。
[8e1f3a96ae] switch UNICODE_BETA back to NO
common.mk
の UNICODE_BETA
が NO
になっています。
[2672502457] mention Unicode Version 14.0.0
NEWS.md
にドキュメントを追加しています。
[9b545b0caf] update specs to check for Unicode Version 14.0.0/Emoji Version 14.0
RbConfig::CONFIG['UNICODE_VERSION']
と RbConfig::CONFIG['UNICODE_EMOJI_VERSION']
に対するテストが追加されています。
これでバージョンを取得できるんですね、へー。
p RbConfig::CONFIG['UNICODE_VERSION'] # Ruby 3.1 => # "13.0.0" # Ruby 3.2 => # "14.0.0" p RbConfig::CONFIG['UNICODE_EMOJI_VERSION'] # Ruby 3.1 => # "13.1" # Ruby 3.2 => # "14.0"
UNICODE_VERSION
と UNICODE_EMOJI_VERSION
で同じバージョンとは限らないんですね、へー。
[9b545b0caf] add some tests for Unicode Version 14.0.0
正規表現の \p
(Unicode プロパティによる文字クラス指定) のと String#upcase
のテストが追加されています。
[45187a0fcd] comment out failing Unicode/Emoji version checks temporarily
テストをコメントアウトしています。何かあったんですかね?
[1b571d0abf] Fix guards for unicode versions specs
テストのコメントアウトを元に戻しています。
[267f0089d3] clarify meaning of version guards for Unicode version specs [ci skip]
テストのインデントを調整したりコメントを追加したりしています。
[56d9d78f14] Remove Unicode 13.0.0 related files
enc/unicode/13.0.0/casefold.h
と enc/unicode/13.0.0/name2ctype.h
を削除しています。
この2つはバージョンごとに新しいファイルを追加している感じですかね?
[48f1e8c5d8] Fix version check to use Emoji version for emoji-variation-sequences.txt
テストで参照しているバージョンを UNICODE_VERSION
から EMOJI_VERSION
に変更しています。
ただ、これは別のコミットで元に戻されているぽいですね。
と、いう感じでコミットを1つ1つみてみました。
実際の Unicode 対応としては、
enc/unicode/14.0.0/casefold.h
enc/unicode/14.0.0/name2ctype.h
lib/unicode_normalize/tables.rb
の3つのファイルが重要そうですね。
今度はこの3つのファイルがなにをやっているのか見ていきたいと思います。
enc/unicode/14.0.0/casefold.h
enc/unicode/14.0.0/casefold.h
には Unicode の大文字、小文字の対応表が定義されています。
例えば Unicode 14.0.0 で追加された U+2C2F
という文字も enc/unicode/14.0.0/casefold.h
に新しく追加されています。
# Ruby 3.2 だとちゃんと小文字に変換される p "\u2C2F".downcase.codepoints.map { _1.to_s(16) } # Ruby 3.1 => ["2c2f"] # Ruby 3.2 => ["2c5f"]
この enc/unicode/14.0.0/casefold.h
ファイルは CaseFolding.txt
を元に指定 enc/unicode/case-folding.rb
で機械的に生成されているみたいです。
enc/unicode/14.0.0/name2ctype.h
enc/unicode/14.0.0/name2ctype.h
には正規表現の POSIX 文字クラスの [:alpha:]
や [:lower:]
や Unicode プロパティなどのメタデータが定義されています。
例えば Unicode 14.0.0 で追加された U+0870
という文字も enc/unicode/14.0.0/name2ctype.h
に新しく追加されています。
p "\u0870" =~ /[[:alpha:]]/ # Ruby 3.1 => nil # Ruby 3.2 => 0
この enc/unicode/14.0.0/name2ctype.h
ファイルは tool/enc-unicode.rb
から機械的に生成されているぽい?
lib/unicode_normalize/tables.rb
lib/unicode_normalize/tables.rb
は enc/unicode/14.0.0/casefold.h
と enc/unicode/14.0.0/name2ctype.h
とは違って新しくファイルが追加されているわけではなくて既存のファイルを更新しています。
lib/unicode_normalize/tables.rb
には Unicode 正規化を行うためのメタデータが定義されています。
例えば NFKC
で正規化した際の ① => 1
という変換テーブルもここで定義されています。
p "①".unicode_normalize(:nfkc) # => "1"
Unicode 14.0.0 で追加された U+A7F2
という文字も lib/unicode_normalize/tables.rb
に追加されており、この文字を正規化すると "C"
になります。
p "\uA7F2".unicode_normalize(:nfkc) # Ryvt 3.1 => "\uA7F2" # Ruby 3.2 => "C"
lib/unicode_normalize/tables.rb
ファイルもまた template/unicode_norm_gen.tmpl
を元にして機械的に生成されているぽいですかね?
まとめ
と、いう感じで簡単にですが Ruby が Unicode 14.0.0 に対応した時になにをしているのかを簡単にまとめてみました。
Ruby 側で対応している項目自体は多い(Unicode のアップデート内容による)んですが全体的に自動生成されるような仕組みがあるようなのですごい。
あと大文字小文字の変換テーブルや正規表現の POSIX 文字クラス、Unicode 正規化のメタ情報も Ruby の処理系で保持しているんですね。
具体的にこのあたりのデータがどう使用されているのかあんまりわかっていないので次はこのあたりの実装がどうなっているのか調べてみるのとこあおもしろそうですねー。
次は Unicode 15.0.0 の対応もはじまると思うのでその時にまたどういうような対応を行っているのか追えるとよいですねー。
2022/03/31 今回の気になった bugs.ruby のチケット
今週は正規表現マッチするときのタイムアウトを導入するチケットがマージされました。
[Feature #17837] Add support for Regexp timeouts
- 正規表現を比較する時にタイムアウトの概念を導入するチケット
- 色々と議論があったが最終的にはタイムアウトの時間を制御する
Regexp.timeout
Regexp.timeout=
が追加された Regexp.timeout
にタイムアウトする時刻(秒)を設定する事で『正規表現を比較する時に設定したタイムアウト秒を越えると例外が発生する』ようになる- デフォルトだと
nil
になっておりnil
だと例外は発生しない
- デフォルトだと
# デフォルトだと nil pp Regexp.timeout # => nil # この場合はタイムアウトしない /A(B|C+)+D/ =~ "A" + "C" * 28 + "X" # タイムアウトする時刻を設定する事ができる Regexp.timeout = 0.1 pp Regexp.timeout # => 0.1 # 正規表現の比較に 0.1秒以上かかると例外が発生する begin /A(B|C+)+D/ =~ "A" + "C" * 28 + "X" rescue => e pp e # => #<Regexp::TimeoutError: regexp match timeout> end
- また
Regexp.new
やRegexp.compile
もキーワード引数timeout:
を受け取るようになたt
# デフォルトだと timeout: nil regexp = Regexp.new("A(B|C+)+D") regexp =~ "A" + "C" * 28 + "X" # 正規表現ごとに個別に timeout を設定することができる regexp2 = Regexp.new("A(B|C+)+D", timeout: 0.5) begin regexp2 =~ "A" + "C" * 28 + "X" rescue => e pp e # => #<Regexp::TimeoutError: regexp match timeout> end
[Feature #12655] Accessing the method visibility
- メソッドの可視性の情報として『
[:public, :protected, :private, :undefined, :overridden]
を取得したい』という要望のチケット :undefined
の情報を取得する手段としてModule#undefined_instance_methods
が新しく追加される予定- PR: https://github.com/ruby/ruby/pull/5733
- まだマージはされていないが matz は accept 済み
class Super def hoge end end class Sub undef hoge end pp Sub.undefined_instance_methods # => [:hoge]
[Feature #15357] Proc#parameters returns incomplete type information
- 以下のようにブロックの引数で『オプショナル引数でないのに
Proc#parameters
で:opt
になる』のは正しくないんじゃないか、というバグ報告
# これは b がオプショナル引数 pr = proc { |a, b = 2| [a,b] } # しかし Proc#parameters は a もオプショナル引数として情報を返す p pr.parameters # => [[:opt, :a], [:opt, :b]] # 同様に以下のような場合も b もオプショナル引数として情報が返ってくる pr = proc { |a = 1, b| [a,b] } p pr.parameters # => [[:opt, :a], [:opt, :b]]
- これは
proc
の引数は全てオプショナル引数として定義されているためである
# オプショナル引数として定義していない a に引数を渡さなくてもエラーにはならない pr = proc { |a, b = 2| [a,b] } p pr.call # => [nil, 2] # こちらも同様 pr = proc { |a = 1, b| [a,b] } p pr.call # => [1, nil]
- なので報告されたコード自体は意図する挙動になっている
- ちなみに
lambda
の場合は必須引数として返ってくる
pr = lambda{|a, b=2| [a,b] } p pr.parameters # => [[:req, :a], [:opt, :b]] pr = lambda{|a=1, b| [a,b] } p pr.parameters # => [[:opt, :a], [:req, :b]]
- また、このように
lambda
であるようにパラメータ情報を受け取る手段としてProc#parameters
にlambda: false
キーワード引数が追加された- これは既に Ruby 3.2 で対応済み
pr = proc{|a, b=2| [a,b] } p pr.parameters # => [[:opt, :a], [:opt, :b]] p pr.parameters(lambda: true) # => [[:req, :a], [:opt, :b]] pr = proc{|a=1, b| [a,b] } p pr.parameters # => [[:opt, :a], [:opt, :b]] p pr.parameters(lambda: true) # => [[:opt, :a], [:req, :b]]
2022/03/24 今回の気になった bugs.ruby のチケット
今週は注釈付き代入演算子を提案するチケットなどがありました。
[Feature #18626] 注釈付き代入演算子 ()= の提案
- 以下のような注釈付き代入演算子の提案になる
class Object # 代入するときの処理をフックする def self.()= (what) what.is_a? self or raise TypeRestrictionError end end # 代入する時に注釈を指定して定義する age (Fixnum) = 30 # メソッドの引数にも定義できる def add(a(Numeric), b(Numeric)) a + b end add 1, "2" # raises TypeRestrictionError
- この提案なんですが以下のような理由から Reject されています
文法的にも意味論的にも大変興味深い提案ですが、Ruby言語にいかなる形であれ型宣言や型注釈を導入するつもりはありません。すみません。
まつもと ゆきひろ /:|)
- これは面白いなー既存の構文を壊さないようにしてなんか定義できないだろうか
[Feature #18644] Coerce anything callable to a Proc
- 呼び出し可能オブジェクトを拡張するためのメソッドを追加する提案
#call
メソッドをProc
化するObject#to_proc
class Object # call メソッドを `Proc` オブジェクト化する def to_proc return method(:call).to_proc if respond_to?(:call) raise "Needs to respond to :call" end end class Proc def to_proc self end end callable.to_proc.curry[value]
- 引数を
Proc
オブジェクト化するKernel#Proc
class Kernel def Proc(value) if value.is_a?(::Proc) value elsif value.respond_to?(:call) value.method(:call).to_proc else raise "Needs to implement :call" end end end Proc(callable).curry[value]
#call
メソッドがあるオブジェクトをProc
化したいケースってどれぐらいあるんですかね
[Feature #18640] default empty string argument for String#sub
and String#sub!
, e.g. "hello".sub("l")
String#sub(pattern, replace)
のreplace
のデフォルト値を""
にする提案
# "hello".sub("l", "") と同じ意味になる p "hello".sub("l") # => "helo"
sub
って名前なのに引数が1つしかないのがちょっと気持ち悪いなあ