The Hobgoblin of Little Minds
Ralph Waldo Emerson famously said, “A foolish consistency is the hobgoblin of little minds, adored by little statesmen and philosophers and divines.” I don’t think he was talking about code, but that statement couldn’t be more relevant to software engineers.
I’ve experienced a scenario like this a number of time in my career:
I’m sharing a new approach to writing code that offers some clear improvements to what we’ve been doing. Perhaps it is more readable, more efficient, or safer. But the response that I hear from colleagues is, “But we can’t do that here. We have <some large number> lines of code where we didn’t do it that way, so it wouldn’t be consistent.”
This is Why We Can’t Have Nice Things
I know what you’re thinking:
That can’t be right. No one would say “No” to a better way of doing something simply because they can’t improve their entire codebase all at once.
In a sense you are right. I really think that most of the time I’ve heard this response, the individual was really thinking:
I’m uncomfortable with change in general and this change in particular, but I don’t want to admit that I don’t like/understand this just because it wasn’t invented by me. If I try to argue against this change on its merits, I’ll reveal that I don’t really understand the issues and I take the chance that I won’t prevail on the merits, so I’ll play the “consistency” trump card to shutdown discussion now.
A Wise Consistency
Don’t get me wrong, I’m all for consistency. It was clearly a mistake that the member function to delete all elements of specific value from a container was called erase for all containers except list, where it was called remove. (This mistake was fixed in C++11, by adding erase to list.)
Accidental inconsistencies are, of course, foolish and unproductive. Leave gratuitous inconsistencies for creative writing. In technical work, figure out the best way to express something and always express it the same way.
Until you learn a better way. Then embrace the improvement. This inconsistency is called growth.
Improvement is Change
The STL was a monumental intellectual achievement, but one flaw was that member predicates were improperly named. Consider the empty container member function. Is it a command to empty the container or a query about the container’s state? You know, but that is because you are already familiar with the library. Naive users must experience the confusion between empty, which is a query and clear, which is a command. There is no a priori way of knowing which is which. They could just as easily be the other way around.
The flaw is unnecessary if the policy that predicates should start with is_ had been followed. The function that is named empty should have been named is_empty and the function that is named clear should have been named empty. (It isn’t obvious that clear means “empty the container,” it could, just as easily, mean, set all element to the default constructed state.)
To be consistent, no predicate in the standard should start with is_. Fortunately the committee elected to be inconsistent when naming the predicates in <type_traits>. Improvements from previous practice are, by their very nature, inconsistent with that practice.
Being consistent means never improving. This is acceptable for people who think that their coding practice is perfect, but not for people that crave continuous improvement.
Imagine driving through a unfamiliar city that had mismatched street signs at every corner. It would be hard to navigate because it would be hard to pick out the street signs amid the visual noise of all the business signage and billboards. That kind of inconsistency could lead to traffic snarls and accidents and should be avoided.
But suppose a city with uniform street signs adopts a new, more readable, standard for signs. The city might not have the budget to replace all the signs overnight. It might start replacing the signs on the busiest streets and using the new signs on newly constructed streets. Over time, less traveled streets would get new signs, but perhaps some residential streets where traffic is slow moving and there is no competing signage, might never be updated.
How hard would it be navigate a city that had two signage styles? This isn’t the challenge of a mismatched signs at every corner, it is simply dealing with the change that is inevitable with improvement.
I’ve not written a post in… well, I don’t want to know… so what got me motivated to vent about hobgoblins and little minds?
Some background: Many years ago I read an article by Dan Saks which recommend placing const after what it modifies. I found the article persuasive. Notice that the rule for const placement is:
const modifies what is on its left. Unless there is nothing on its left, in which case it modifies what’s on its right.
If you consistently place const after what it modifies, the rule becomes much simpler:
const modifies what is on its left.
(I love simple, consistent rules.)
It also make declarations (which are read inside-out and right to left) easier to read. This isn’t the place to list all the arguments that Dan shared, but they convinced me. For years, I’ve followed this practice. I knew that I was in a stylistic minority, but I thought it was a better way to write code so that is what I did.
Recently, I’ve noticed that more and more C++ programmers are starting to adopt this style. In fact, a few months ago, I learned that it had a catchy name: East const.
What prompted this posting was a tweet with a link to this core guideline: NL.26: Use conventional const notation. You can read it yourself, but it essentially says, don’t use East const. The guideline concedes that East const is more logical, but since it is less common, it is forbidden.
As I said, I’ve known for years that I was in the minority and I’m okay with it if you say:
This is how we wrote code back in the Eighties and we liked it!
But to condemn a more logical approach because of inconsistency? Well, you know what Ralph Waldo Emerson said about it.
It really gets me that after invoking consistency, the guidelines authors hide behind novices. They are in effect saying, we need to compromise the quality of our code instead of improving our training materials.
What puzzles me is the enforcement:
const used as a suffix for a type.
How is one supposed to declare an immutable pointer that references a mutable int? I might do it like this:
int * const p;
But the type of p ends in const. Perhaps there is another way to declare it that doesn’t have a const suffix. If so you won’t see it in this core guideline because one of its “OK” examples also ends in const. Or perhaps the enforcement engine will just have a complicated rule.
I don’t like complicated rules, but some people seem to like them for const.
If you’d like to share your thoughts, please comment on the reddit post. (I’m well aware that this is a rant, but please keep comments constructive and professional. Thanks.)