There is a another reason why class and def create fresh scopes, related
to the way Ruby resolves method / local variable ambiguity.
One of the benefits of Ruby is its lack of verbosity: in particular no
need to declare variables before using them, and no need to put ‘()’
after a method call if it doesn’t have any arguments. The result is
compact and readable code.
However it leaves an ambiguity: is a bareword like “x” to be interpreted
as a method call, i.e. self.x(), or as referring to a local variable?
This is resolved statically at parse time with a simple rule. If an
assignment expression “x = …” has been seen earlier in the current
scope (regardless of whether it is actually executed), then a bareword
“x” is a local variable, otherwise it’s a method call.
So:
def x
“hello”
end
def foo
if false
x = “bye”
end
puts x # prints nil (local variable, no value assigned)
end
This is unusual, but once you’ve used it, it makes perfect sense.
Now consider what would happen if ‘def’ didn’t start a new scope.
x = 123
… 1000 lines of code
def x
“hello”
end
def foo
puts x # 123 instead of hello?
end
The code would behave differently, because of the binding to the ‘x’
outside.
For me, the important thing is this: within the method body of ‘foo’, I
can tell for sure whether x is a local variable or a method call, by
inspecting only the code within that method. If it weren’t for this
flushing of the scope, then I couldn’t be sure without reading the
whole of the rest of the source file up to that point.
The same argument applies to a class, and class methods:
silly example
memoize = 99
… 1000 lines …
class Foo
extend Flurble
memoize
end
Just by inspecting the body between ‘class Foo’ and ‘end’, I can be sure
that memoize must be a method call - perhaps something in module
Flurble, or else some method already added to class Class. Without this
scope flushing, I would have to scan the whole of the rest of the source
file.
This behaviour may not sit comfortably with users of certain other
programming languages. I say that’s fine. Ruby is a different language,
and has different idioms. If you are more comfortable with Lisp or
Haskell, then use those instead.
There are of course pros and cons to each way of doing things, but if
you open your mind to new ways of working, you may find the pros are
surprisingly substantial. (And I’m sure the same is true when moving in
the opposite direction too