Feature #16432
closedUsing `_1` inside `binding.irb` will cause unintended behavior
Description
Summary¶
Calling binding.irb
in a block that uses _1
and using _1
in irb
will cause unintended behavior.
Steps to reproduce¶
- Use
_1
in block - Use
binding.irb
in block - Use block with
_1
inbinding.irb
# test.rb
proc {
binding.irb
_1
}.call 42
# binding.irb
irb> (1..10).map { _1 + _1 }
Expected behavior¶
irb> _1
=> 42
irb> (1..10).map { _1 + _1 }
= > [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
Actual behavior¶
- Refers to the
_1
of the block that calledbinding.irb
irb> _1
=> 42
irb> (1..10).map { _1 + _1 }
= > [84, 84, 84, 84, 84, 84, 84, 84, 84, 84]
I think this is an unintended behavior for the user.
Updated by nobu (Nobuyoshi Nakada) almost 5 years ago
It is same as the following code without irb.
proc {
p eval("(1..10).map{_1+_1}") #=> [84, 84, 84, 84, 84, 84, 84, 84, 84, 84]
_1
}.call(42)
This is because the eval
code is executed at the current context exactly.
Another choice is to raise a syntax error at the runtime.
Updated by osyo (manga osyo) almost 5 years ago
Thanks comments :)
This is because the eval code is executed at the current context exactly.
Another choice is to raise a syntax error at the runtime.
Yes, and if _1
is nested, a syntax error will occur.
proc {
_1
# error: numbered parameter is already used in
(1..10).map { _1 + _1 }
}.call 42
If _1
is nested, I think it is better to runtime error rather than unintended behavior.
# Expected behavior in case of runtime error
# OK
irb> _1
=> 42
# OK
irb> _1 + _1
=> 84
# NG: Runtime error
irb> (1..10).map { _1 + _1 }
Updated by ko1 (Koichi Sasada) almost 5 years ago
- in short:
1.times{
p _1 #=> 0
eval("[:a, :b].each{p _1}")
#=> 0
#=> 0
}
mame: it can cause confusing bug.
ko1: we have two solutions:
- separate numbered parameter scope between in/out eval.
- Issue: we can't see
_1
variables from debugging reason.- allow
Binding#local_variable_get("_1")
for this purpose? How to get uplevel binding?
- allow
- Same semantics without
eval
.
- issue: we can't write numbered parameters in this kind of context.
# 2.7.0 behavior
1.times{p _1
eval('p _1') #=> OK (0)
eval('[:a].each{p _1}') #=> OK, but confusing (0)
}
# 1.
1.times{p _1
eval('p _1') #=> NameError
eval('[:a].each{p _1}') #=> OK (:a)
}
# 2.
1.times{p _1
eval('p _1') #=> OK (0)
eval('[:a].each{p _1}') #=> SyntaxError
}
1.times{binding.irb}
# start IRB session
irb> p _1
#=>
# 2.7.0 NameError
# 1. NameError
# 2. NameError
1.times{binding.irb;_1}
# start IRB session
irb> p _1
#=>
# 2.7.0 OK (0)
# 1. NameError
# 2. OK (0)
irb> [:a].each{p _1}
#=>
# 2.7.0 OK, but confusing (0)
# 1. OK (:a)
# 2. SyntaxError
matz: vote for 1
Conclusion:
- Solution 1. Nobu will fix parse.y
- hopefully backport to 2.7.1 (March 2020)
Updated by nobu (Nobuyoshi Nakada) almost 5 years ago
- Status changed from Open to Closed
Applied in changeset git|c171ab23e376b6c7f1094a77f137d916b0a403e6.
Separate numbered parameter scope in eval
[Feature #16432]
Updated by nobu (Nobuyoshi Nakada) almost 3 years ago
- Related to Bug #18558: Numbered arguments + eval added