From: "jeremyevans0 (Jeremy Evans) via ruby-core" Date: 2025-07-28T20:21:13+00:00 Subject: [ruby-core:122878] [Ruby Bug#21139] Prism and parse.y parses `it = it` differently Issue #21139 has been updated by jeremyevans0 (Jeremy Evans). vinistock (Vinicius Stock) wrote in #note-18: > Thanks for the context, I understand your point about consistency. So this case > > ```ruby > 42.tap { |x| x = x; p x } > ``` > > Works because by the time we reach `x = x`, the block parameter already declared `x` as a local and so it doesn't end up setting it equal to `nil`. Would it make sense to automatically declare `it` as a local inside the block? > > Or another question, do we agree that these two should return the same result? > > ```ruby > 42.tap { it = it; p it } > 42.tap { |x| x = x; p x } > ``` No. `it` is not a local variable until it is referenced, so the first example should always print `nil` (unless `it` was already a local variable in higher scope). Everywhere else in Ruby: ```ruby lvar = lvar ``` Will result in `lvar` being nil if was not already a local variable in scope. To think that `42.tap { it = it; p it }` should print 42 is to believe that `it` is a local variable before it is referenced. However, if `it` is never referenced, then `it` is never a local variable, and the block does not accept an argument: ```ruby proc{}.arity # => 0 proc{it}.arity # => 1 ``` You could argue that `it` is a local variable from the start of the block instead of where `it` is first referenced in the block. However, that's not how local variables work in general. Every other place in Ruby where a local variable is declared (referenced in the LHS of an assignment), it is only considered declared after the reference, not before the reference: ``` def x = 1 def y x # => 1 x = x x # => nil end ``` If `it` should be considered a local variable from the start of the block if it is referenced anywhere in the block, then in the above example, the first reference to `x` inside `y` should be a local variable reference instead of a method call, even though the `x` local variable is not declared until afterward. In general, you shouldn't consider `it` to be a local variable. If `it` was a local variable, this would work: ```ruby proc{eval("it")}.call(1) # NameError ``` But it doesn't work, even if `it` is already referenced: ```ruby proc{it; eval("it")}.call(1) # NameError ``` If at the point of reference, `it` is already a local variable, Ruby treats it as it would any local variable. If `it` is not already a local variable, then Ruby treats it as the implicit block parameter. Inside a block, for `it = it`, the RHS `it` was previously declared as a local variable by the LHS, so it is treated as a local variable instead of as the implicit block parameter. AMomchilov (Alexander Momchilov) wrote in #note-19: > @jeremyevans0 I see your point about how ivars take precedence over methods of the same name, but I think `it` is more like a block argument (well, a local variable) than a method, so I find this inconsistently really surprising: > > ``` > ruby --parser=parse.y -e "42.tap { it = it; p it }" # => nil > ruby --parser=parse.y -e "42.tap { |it| it = it; p it }" # => 42 > ``` > > Since `it` is just a shorthand for "the first block argument," then I think people would expect that it works _as if_ you declared `|it|` yourself. They may. However, doing so is inconsistent with how local variables are designed in Ruby. Personally, I think consistency is much more important in this case. I cannot think of a good reason to define an `it` local variable while also using `it` as the implicit block parameter. If you are only using `it` in the same block scope, no local variable is necessary. If you are setting `it` to a local variable so it is usable inside a nested block, you should pick a different variable name, as using `it` is more likely to cause confusion. ---------------------------------------- Bug #21139: Prism and parse.y parses `it = it` differently https://bugs.ruby-lang.org/issues/21139#change-114181 * Author: tompng (tomoya ishida) * Status: Feedback * Assignee: prism * ruby -v: ruby 3.5.0dev (2025-02-14T16:49:52Z master ee181d1bb7) +PRISM [x86_64-linux] * Backport: 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- ~~~ # ruby --parser=parse.y -e "42.tap { it = it; p it }" nil # ruby --parser=prism -e "42.tap { it = it; p it }" 42 ~~~ ---Files-------------------------------- clipboard-202503081702-idzz2.png (22.6 KB) -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/