💬
[Bug #20965] binding.local_variables に it が含まれないバグ報告
[Bug #20965] it vs binding.local_variables
- 以下のように
binding.local_variablesの結果が_1とitで異なっているというバグ報告
# _1 も it も使用していない場合はなにも返さない
p(proc { binding.local_variables }.call) # []
# ブロック内で _1 を参照している場合は :_1 が含まれる
p(proc { _1; binding.local_variables }.call) # [:_1]
# 一方 it の方は参照していても :it は含まれない
p(proc { it; binding.local_variables }.call) # []
- これなんですが一貫性を保つために
:itを含めるように開発版の Ruby 3.4-dev で対応がされました- 厳密にいうと
itがブロック内で参照されているとローカル変数のように振る舞うようになった
- 厳密にいうと
- っていうのが 2024/12/19 時点の話になります
- その直後に以下のコードが parse.y と prism で差異があることが起票されました
i = 2
42.tap do
p it # => 42
p local_variables # => [:it, :i]
p it /1/i
# parse.y => 21
# prism => NoMethodError
end
- これなんですがどういうことかというと
- parse.y =>
itが変数として定義されているのでit / 1 / iという除算として処理される - prism =>
itがメソッドとして解釈されit(/1//i)と処理される
- parse.y =>
- そもそも
hoge /1/iみたいな書き方は Ruby だと曖昧でhogeがどう定義されているのかに依存します
hoge = 42
i = 2
# 変数がある場合は除算として処理される
p(hoge /1/i) # => 21
def hoge(x) = x
i = 2
# メソッドが定義されている場合はメソッド呼び出しとして処理される
p(hoge /1/i) # => /1/i
- 今回の
[Bug #20965]の対応によってitの定義が変わり意図しない挙動になってしまったみたいです- ちなみに
[Bug #20965]の対応前は両方ともNoMethodErrorになっていました
- ちなみに
-
itが呼び出し方によって変数なのかメソッドなのかが構文からは決定できないのがポイントみたいですねえ - また、次のような点も指摘されていますね
"foo".tap do
it
"bar".tap do
# このコードを実行した時に例外が発生すること期待していると(実際今は例外になる)
p eval("it")
# it 変数が参照できないのに [:it] を返すのが奇妙になる
p binding.local_variables
end
end
- などなど Ruby 3.4 のリリース直前もあり
binding.local_variablesが:itを返す対応は一旦 Revert されました - いやー直前で対応がされてよかった Ruby むずかしいね
- そういえばこれを検証ている時に気づいたんですが
_1はネストして参照できないんですがitってネストして参照できるんですね?
"foo".tap do
p _1
"bar".tap do
# SyntaxError: numbered parameter is already used in outer block
p _1
end
end
"foo".tap do
p it # => "foo"
"bar".tap do
p it # => "bar"
end
end
- これって意図されているんですかね?と思ってたら Bug #20930: Different semantics for nested
itand_1で議論されており、これは意図する挙動みたい-
itは一番内側のブロックの引数を参照する
-
Discussion