Economizing can be penny-wise and pound foolish
or,
“How I learned to stop worrying and love the separation of accidental and actual complexity in programs.”
Reinier Zwitserloot said
something interesting:
What’s worth more? The macro facilities or the provably linear-time chasability of code identifiers and the like? All I know is: I have never read a satisfactory answer to the dilemma, merely a boatload of silent assumptions that the expressive power must be better… somehow.
I’m a simple man, and what matters to me is the amount of time it takes me to design code, to write code, to debug code, to understand the code that others have written, and to write code that others will understand.
The one factor that has risen above all others for these tasks is the
signal to noise ratio in code. This idea has been far, far better explained as the difference between “accidental” and “inherent” (or “actual”) difficulty in programming.
Pick up the source code for a program and three highlighter markers, one green, one yellow, and one red. Go through the source. If you don’t understand what something does, mark it in red. If you understand it, but it has everything to do with the accidental difficulty of the implementation and nothing to do with the inherent difficulty of the problem, mark it in yellow. And finally, if something seems to express the problem and its solution fairly directly in an manner you understand, mark it in green.
What could be simpler? We want more green, less yellow, and absolutely no red in our programs. We can’t escape the yellow marker: if we like (I only say
if we like) Ruby on Rails, we like the fact that our code has lots of green, because it’s written in a DSL aimed at CRUD applications on the web. But if we look inside Rails, we see lots of stuff that is yellow or—horribly for us—red. The yellow under the covers makes the green in our code possible.
(Ad: Raymond Smullyan introduces Godel's incompleteness theorems and some interesting related results though puzzles about knights, knaves, and their ability to become self-aware.
) (I dare say that this was once the philosophy behind enterprisey stuff like SOAP and J2EE. Somebody, somewhere felt that these frameworks could help programmers write stuff that focused on the task at hand and left the accidental difficulties, the “gory details” as they were, to the framework.)
ConcentrationThis is the principle behind structured programming and every programming language where we can build our own abstractions.
It’s not that we eliminate the yellow. Rather, we shift it away from the green so that there's one nice place where everything is green: we say that this is our solution, this is our idea. And there's this other mass of yellow that makes the green work, we say this is our infrastructure, this is the hardest working code in show business.
The opposite of this is where the green and yellow is all mixed up. This is why
Design Patterns are a sign of weakness, not of strength. With a pattern, you have yellow code intermingled with your green code. You may be so used to it you think you don’t see the yellow, but nevertheless your Visitor or your Singleton or your Adapter is there taking up space and diluting your ideas.
At a finer level, this is why iteration—especially when we maintain our own index variables—is weaker code than mapping and folding. Yellow code like
++i;
is sprayed all over our problem logic.
When the green is all in once place, we can look at it and verify it and think about it with our
Inherent Difficulty hat on. And by segregating all of the yellow code into its own place, we can look at it and verify it and think about it with our Accidental Difficulty or
Implementation hat on.
Further to that, my experience is that our green code sometimes changes in really fundamental ways. Requirements change, our understanding changes, and we must make drastic changes to the fundamental logic behind our programs. When the green is in one place, you change it and—cross your fingers—you’re done.
The yellow code is as obstinate and coupled and messy as ever, but you don’t touch it. When your code is intermingles, the slightest change to your program logic breaks all of your infrastructure and patterns in unpredictable ways. Coupling in yellow code is bad, but coupling yellow code to green code is hideous.
That's the appeal of Domain-Specific Languages and Metaprogramming. When done right, there's no less implementation or accidentally difficult code, but it’s sharply separated from the code that solves the inherent difficulties of our problem.
So, does size matter?I’m very comfortable with the idea that some languages enable more green and less yellow through sheer economy of expression. But I’m also comfortable with the idea that other languages may offer more green and less yellow or more separation of green and yellow through different mechanisms.
If two programs do much the same thing and one is smaller than the other, I’m not going to jump right up and say that the former is
necessarily better or that the former’s language/framework/libraries are necessarily better.
At the moment, I want to look at them both and start colouring. If the longer program has more green code, or has it all in one place, or if the shorter program is marred by red… I’m inclined to say the short one isn’t the best we can do.
So… if expressiveness is measured by economy of expression, I don’t think it is always better. And should we pursue economy at the expense of separating the solution to inherent and accidental difficulties, I think we make our code less worse, not better.