Moving Away from State: State–
In 1968, Edsger Dijkstra issued a letter entitled “Go To Statement Considered Harmful,” and the age of structured programming was born. The letter was a call to stop programmers of the time from creating ad-hoc control flow structures using goto statements and to instead use these higher level constructs for manipulating flow through a function (contrary, I think, to the oft-attributed position that “goto is evil”). This gave rise to structured programming because it meant that progress in a method would be more visually trackable.
But I think there’s an interesting underlying concept here that informs a lot of shifts in programming practice. Specifically, I’m referring to the idea that Dijkstra conceived of “goto” as existing at an inappropriate level of abstraction when stacked with concepts like “if” and “while” and “case.” Those latter are elements of logical human reasoning while the former is a matter of procedure for a compiler. Or, to put it another way, the control flow statements tell humans how to read business logic, and the goto tells the compiler how to execute a program.
“State Considered Harmful” seems to be the new trend, ushering in the renaissance of functional programming. Functional programming itself is not a new concept. Lisp has been around for half a century, meaning that it actually predates object-oriented programming. Life always seems to be full of cycles, and this is certainly an example. But there’s more to it than people seeking new solutions in old ideas. The new frontier in faster processing and better computer performance is parallel processing. We can’t fit a whole lot more transistors on a chip, but we can fit more chips in a computer — and design schemes to split the work between them. And in order to do that successfully, it’s necessary to minimize the amount of temporarily stored information, or state.
I’ve found myself headed in that direction almost subconsciously. A lot of the handiest tools push you that way. I’ve always loved the fluent Linq methods in C#, and those generally serve as a relatively painless introduction to functional programming. You find yourself gravitating away from nested loops and local variables in favor of chained calls that express semantically what you want in only a line or two of code. But gravitating toward functional programming style goes beyond just using something like Linq, and it involves favoring chains of methods in which the output is a pure, side-effect-free function of the input.
Here have been some of the stops on my own journey away from state.
- Elimination of global state. The first step in this journey for me was to realize how odious global state is for the maintainability of an application.
- No state variables for communication between methods. Next to go for me were ‘flag’ variables that kept track of things in a class between method calls. As I became more proficient in unit testing, I found this to be a huge headache as it created complicated and brittle test setup, and it was really a pointless crutch anyway.
- Immutable > mutable. I’ve blogged about an idea I called “pointless mutability,” but in general I’ve come to favor immutable constructs over mutable ones whenever possible for simplicity.
- State isolation — for instance, model objects and domain objects for business logic state and viewmodels/controllers for GUI state. Aside from that, a lot of applications for which I am the architect retain virtually no state information. Services and other application scaffolding types of classes simply have interfaced references to their collaborators, but really keep track of nothing between method calls.
- Persistence ignorance — letting the application pretend that its state is storage. For less sophisticated (CRUD-style) applications, I’ve favored scenarios in which most of the code’s state is abstracted into a lower layer of the application and implemented only externally. In other words, if things are simple, let something like a database or file system be your application’s state. Why cache and over-complicate until/unless performance is an issue?
And that’s where I stand now as I write object-oriented code. I am interested in diving more into functional languages, as I’ve only played with them here and there since my undergrad days. It isn’t so much that they’re the new hotness as it is that I find myself heading that way anyway. And if I’m going to do it, I might as well do it consciously and in directed fashion. If and when I do get to do more playing, you can definitely bet that I’ll post about it.