Why are local variables bad?
Steve Yegge wrote a terrific post about
refactoring, "
Transformation," in March of 2006. My two cent review is that the most valuable part is his discussion of how the
Blub community has become so enamored of push-button refactoring at the line of code level that they have lost sight of what refactoring is supposed to accomplish at the design level.
Many Blub programmers won't even
try a language that doesn't have so-called refactoring support in their IDE. Given
the choice between dynamic metaprogramming and refactoring, they choose refactoring without even knowing exactly what
dynamic metaprogramming is.
And they are blissfully unaware that the 'refactorings' in their editors are just a few of the
refactorings suggested in Martin Fowler's seminal book on the topic. Furthermore, there is more than one way to accomplish the goal of well-designed software, and some languages, (especially folding languages), provide much more powerful tools for clean software design than micro-refactoring.
All that leads me to a question posed on
programming.reddit.com:
Why are local variables considered bad?I'll simplify things.
Mutable local variables are bad. And here's why:
Mutable local variables mean that within a method execution you have changes of state. That makes it very difficult to change anything without breaking things.
Those are my principles, and if you don't like them... well, I have others.
Groucho Marx
For Eclipse fans, consider what happens if you take a random chunk in the middle of a method with mutable local variables and try to use the automatic refactoring to make a new private method.
If you have lots of immutable local variables, they have to be passed to your new method as parameters. That's not so bad. But if you have mutable local variables, you have to do some back flips and sit ups to ensure that any changes to those variables are propagated to the rest of your method.
This is a tip-off that the mutable local variables have introduced a lot of complexity to the structure of the method. Unless they make it dramatically shorter than a variable-free version of the same method, they should be removed.
(Update): What kind of complexity are we talking about? We are talking about lots and lots of dependencies between the lines of code in the method. Those dependencies are what make the code brittle: if you try to move something or change it, you break some other code that is depending on its side effects. The extra dependencies have introduced
coupling, which is a code smell.
The book that touched off a revolution in software design. Incredibly, most programmers think that refactoring is just about the dozen or so menu items in their IDE. This book discusses over a hundred refactorings, organized into themes. It goes beyond mere transformations to explain what makes good code and how to recover from bad decisions.The extra coupling manifests itself in many ways, not just in the difficulty of moving code around. You also have problems changing lines of code. This manifests itself in regressions: every time you fix a problem or add some functionality, do you find yourself having to fix a bunch of bugs created by your change?
Let's be clear: we don't say mutable local variables are bad because it's hard to refactor methods with mutable local variables. We say mutable local variables are bad because they intrduce complex dependencies and coupling between the lines of code in a method.
Immutable local variables, such as the local variables you use to cache certain results, are
not bad. (I should say, "in my opinion," but this is my blog. Note the words "my" and "blog," establishing that we're talking about
me and
opinions, not everyone and especially not widely accepted irrefutable facts).
As a matter of fact, I can
prove that they are not bad. Okay, "proof" is an ambitious word given that there is no widely accepted
proof that 1+1=2. I'll demonstrate. Here's some code with an immutable local variable:
my_var = SomeModelClass.find :all
if my_var.empty?
:empty
else
my_var
end
Let's transform that code into:
(lambda do |my_var|
if my_var.empty?
:empty
else
my_var
end).call(SomeModelClass.find :all)
We've transformed the immutable local variable into a parameter for a lambda (or closure, or anonymous proc object, whichever name you prefer). Parameters are not harmful. And since this transformation can be automatically performed for
any immutable local variable, I claim that immutable local variables are also not harmful.
Cool discovery, hunh? I should be famous. Oh wait, someone on the phone from the 1970s. The Lisp people want their
let macro back. And they're right. People having been using
let for more than thirty years to write concise code without the side effects and state changes associated with mutable local variables. Consider instead:
(let ((my_var (find SomeModelClass 'all)))
(if
(empty? my_var)
'empty
my_var))
That's simpler structurally than using one of Ruby's procs. So what do we conclude?
First, refactoring is cool, but it's a tool, not an objective. The important thing is the objective, well-designed software. Second, read the book. Really read it, cover to cover.
Update: This is exactly why languages with more powerful abstractions are more important than adding push-button variable renaming to less powerful languages. Where's the button that refactors a method with lots of mutable variables into one with no mutable variables? No button? Okay, until you invent one, don't you want a language that makes it easy to write methods and functions without mutable local variables? (Attention Java programmers: Yes you do, and I have proof. Do you like the new for syntactic sugar in Java 1.5? Its biggest benefit is that it makes loops that used to have a mutable variable look just like an iterator block with an immutable parameter in Ruby or Smalltalk. Think about it.)Labels: popular