Disallow shadowing variables

Here’s another wart from ruby that has bitten me in the past, variables that “shadow” outer variables:

a = 3
[4].each{ |a|
  puts a
}

I think I sometimes don’t realize I’m shadowing an earlier variable and so it confuses me and can cause weird bugs (of course). In my ideal world I’d say disallow this pattern altogether :slight_smile: thoughts?

1 Like

FWIW Ameba catches this.

1 Like

Nothing to see here, the block issued a breaking-change PR. Now the local variable has to suffer.

This was disallowed in the past and we eventually allowed it. It was bothering sometimes not being able to use an intuitive name in an inner scope.

1 Like

Honestly that’s true everywhere, not in Ruby specifically. Introducing a new lexical scope generally allows you to use that namespace however you please. Maybe a better way to deal with that would be to name things a bit less concise? If you describe the use of memory address you are less likely to hit a collision, at least that’s my experience.

I think matz lists this as one of the things he could remove from ruby.

As far as I understand that’s not some feature you remove, that’s a special case consistent with the ruleset of name lookups.

You may choose to add code to disallow this special case, but then the question becomes why is this case special? Why the first is fine, but the second is not?

def add_one(i)
[1,2,3].each { |i| i+1 }

If you think allowing block’s local scope override previous levels is wrong, why are you okay with children silently overriding parent’s methods? That’s just one example.

To me it can cause a bit of confusion “what’s i again?” leading to, at times, subtle bugs. It’s bit me anyway. I realize there are other examples that (in my mind) could possibly be “clarified” (disallowed?) as well, this was just the one that came to mind most quickly :) Cheers!

I guess there’s room for an optional compile mode that reports warnings on name conflicts. Not just in blocks, but anywhere at all where name resolution is ambiguous to humans, even something like this

def a
  123
end
a = 321
class A
  def self.a
    444
  end
  1.times { |a| puts a }
end

On the other hand, maybe that’s an IDE’s job? Point a cursor at the name and let it work its analysis what does the name mean.

2 Likes

I think that was exactly the other case I was trying to remember, thank you! When things are ambiguous, basically. Warnings would be great :)

I would like that feature.

1 Like