One Thought at a Time

Programming Is About State

... there was an argument early on about was this going to be a functional language. By which was meant at the time: did it have side effects? But ultimately, I didn't go along with that, because I believe programs fundamentally have to do with modifying state.

Barbara Liskov, "The Power of Abstraction"

State is unavoidable in programs. The world is stateful. So are our common understandings of the world, and the languages we use to describe it. Hardware is stateful, and so are machine languages. Most useful programs have to deal with state, from registers and memory to the I/O which makes a program's work observable. There are reasons why most programmers find state intuitive, most programming languages are stateful, and even languages touted for statelessness have built stateful "escape hatches."

Even if we can't avoid state, it is sometimes useful to ignore it, using stateless abstractions which allow programs to specify state changes implicitly. A key example is provided by algebraic expressions: instead of writing something like MOV and ADD instructions from an assembly language, one may write '4 + 5 + 6'. But stateless abstraction has a cost when the underlying statefulness leaks through in observable effects that matter, because the abstraction has made those effects harder to predict and control. When this cost is too high, it is better to bypass the abstraction.

One strategy for state management is to push as much work as possible into stateless abstractions, to contain the state we can't avoid. This is useful, but it's not sufficient or absolutely necessary. It is not sufficient because there will always be tasks where we are forced to use state. It is not necessary because state per se is not a problem. Typical tangles like the overuse of global variables, or non-determinism due to implicit state-sharing between threads, are both symptoms and causes of problems not inherent to state.

Stateless abstraction has a point of diminishing returns, where further investment no longer earns its keep. This point might depend on factors like the costs of abstraction relative to requirements, the cognitive load and readability penalties of the abstractions, and how much is contributed toward the control of undesirably stateful behavior. After a certain point, we may be pursuing statelessness out of dogma, while creating problems that are about as severe as what we set out to avoid.

When stateful behavior cannot reasonably be avoided or ignored, it must be managed. Programmers need ways of predicting and controlling stateful behavior without eliminating it, hiding it, or rephrasing it in stateless terms.