Thinking Forth


About 20 years ago I first read the book Thinking Forth by Leo Brodie, and it taught me a great deal about how to approach software design and how to factor problems. Looking through the book yesterday, it struck me how very well its design advice still holds up, even if the tools of the day are Java and C# instead of Forth...



Let's look at a few examples of the wisdom Brodie was pitching here:

"Generality usually involves complexity. Don’t generalize your solution any more than will be required; instead, keep it changeable." I don't know about you, but I've seen that kind of overkill many times over the years. I've seen frameworks so over-developed and sluggish that the programmers expected to use them spent a lot of effort avoiding them. For acceptable performance, it became necessary to implement a parallel, task-specific body of code. This sounds a lot like the Extreme Programming tenet of "you're not gonna need it," doesn't it?

"You don't understand a problem until you can simplify it." Seriously, how many times have you spent hours coding something tedious, when all of a sudden a cog slips somewhere in your brain and you see a far simpler, cleaner way of doing the same thing? When this happens, it may be a sign that you didn't understand the problem well enough before writing the code; it's much more efficient to have this flash of insight during the design phase of a project.

"Start simple. Get it running. Learn what you’re trying to do. Add complexity gradually, as needed to fit the requirements and constraints. Don’t be afraid to restart from scratch." Again, Brodie seems to be presaging some ideas behind various agile programming practices. Brodie isn't advocating starting a project without any planning here, in case the lack of context is misleading. Rather, he's discussing an iterative approach to software development as a preface to discussing planning, both its importance and limitations, and pointing out the importance of prototyping.

Throughout the book, there are numerous quotes from the creator of the Forth language, Charles Moore. While some of these don't have much to do with more mainstream computer languages, they reveal a philosophy of simple and elegant software design that should give us pause. When asked how long a Forth word (a function) should be, Moore responds, "A word should be a line long. That's the target. When you have a whole lot of words that are all useful in their own right --- perhaps in debugging or exploring, but inevitably there's a reason for their existence --- you feel you've extracted the essence of the problem and that those words have expressed it. Short words give you a good feeling." Features of Forth (such as its implicit parameter passing) encouraged such granularity and relentless factoring of a problem, but the philosophy has wider application.

Regardless of the programming language you use, factoring into small, reusable functions is a vital craft. Brodie offers several tips about factoring to get one thinking: "Factor at the point where you feel unsure about your code (where complexity approaches the conscious limit)... Factor at the point where a comment seems necessary... Limit repetition... When factoring out duplicate code, make sure the factored code serves a single purpose." The chapter on factoring is packed with good advice, even if you ignore the examples specific to Forth.

I remember having to rework someone's serial device driver some years ago. The driver was written in 80x86 assembly language, and the author used numerous flags to keep track of the communications state. The result was a bug-ridden nightmare; the driver would encounter an unusual packet and flip the flags into some impossible, nonsensical state. The solution in the rewrite was a return to first principles, figuring out the valid states the transmitter and receiver could occupy, and maintaining a single state variable for each. Brodie seems to have been there, too. From the Data Handling chapter: "When the application requires handling a group of conditions simultaneously, use a state table, not separate variables."

You might be surprised to note a chapter on minimizing control structures. Say what? What's wrong with those? Well, we've all seen the evil that ensues when somebody gets lazy with factoring and nests "if" statements 40 levels deep. Brodie adds this: "The use of conditional structures adds complexity to your code. The more complex your code is, the harder it will be for you to read and to maintain. The more parts a machine has, the greater are its chances of breaking down. And the harder it is for someone to fix." Moore opines, "Every conditional should cause you to ask, 'What am I doing wrong?'"

The strategies for minimizing control structures presented by Brodie don't apply equally well to all languages, but he still comes up with some good tips: "Don’t test for something that has already been excluded... Combine booleans of similar weight... Choose the control structure that most closely matches the control-flow problem... Don’t decide, calculate [when the conditionals involve numerics]."

It may seem weird to review a 20-year old book that largely centers around a niche language, but to this day Thinking Forth remains one of the best and most enjoyable books I've read on factoring and solving problems. And it's available free on SourceForge under a Creative Commons license thanks to Leo Brodie and the collaborative efforts of Bernd Paysan and others.

Thinking Forth can help clarify your thinking in any computer language.

Posted: Fri - March 4, 2005 at 06:54 PM          


©