Senger CodeLab πŸš€

How can you do anything useful without mutable state

September 29, 2026

πŸ“‚ Categories: Programming
How can you do anything useful without mutable state

In the world of programming, the concept of mutable state often reigns supreme. Variables are updated, values are changed, and data transforms fluidly throughout the execution of a program. But what if we challenged this fundamental principle? How can you do anything useful without mutable state? It may seem counterintuitive, especially for those accustomed to traditional imperative programming paradigms. However, the rise of functional programming and its emphasis on immutability has demonstrated not only the feasibility but also the significant advantages of building software without relying on mutable state. This approach offers a path towards cleaner, more predictable, and ultimately more robust code.

Understanding Mutable vs. Immutable State

Mutable state refers to data that can be modified after its creation. Think of it like a whiteboard where you can erase and rewrite information as needed. This flexibility is powerful but comes with the risk of unintended side effects and difficulties in debugging complex systems. Conversely, immutable state means that once data is created, it cannot be changed. Imagine etching information onto a stone tablet – permanent and unalterable. This immutability, while seemingly restrictive, provides significant benefits in terms of predictability and maintainability.

This fundamental difference impacts how programs are designed and executed. With mutable state, a program’s flow often revolves around updating variables and tracking these changes. Without mutable state, the focus shifts to creating new data based on existing data, leading to a more declarative style of programming.

The Power of Functional Programming

Functional programming is a paradigm that emphasizes immutability and the use of pure functions. Pure functions always produce the same output for the same input and have no side effects, meaning they don’t modify any external state. This characteristic simplifies reasoning about code and makes it easier to test and debug. Languages like Haskell, Clojure, and Elixir are primarily functional, while others, such as JavaScript and Python, incorporate functional features.

Consider a simple example of adding two numbers. In an imperative approach, you might update a variable with the sum. In a functional approach, you’d create a new value representing the sum without altering the original numbers. This seemingly small difference has significant implications for building complex systems.

Here’s how you might add two numbers in a more functional style in Python:

def add(x, y): return x + y result = add(5, 3) result is 8, original values remain unchanged 

Managing Data Without Mutation: Persistent Data Structures

A key challenge in building systems without mutable state is efficiently managing data updates. Persistent data structures offer a solution. These structures allow for “updates” by creating new versions of the data while preserving the original, creating a history of changes rather than overwriting existing data. This approach is crucial for concurrency and version control.

Examples of persistent data structures include:

  • Linked Lists
  • Trees
  • Vectors

By leveraging persistent data structures, developers can enjoy the benefits of immutability without sacrificing the ability to represent changing data over time. Libraries like Immutable.js and Mori provide efficient implementations of these data structures for JavaScript.

Practical Applications of Immutability

Immutability isn’t just a theoretical concept; it has practical applications in various areas of software development. For example, in React, a popular JavaScript library for building user interfaces, the state of components is often managed immutably. This approach simplifies state management and makes it easier to reason about how changes propagate through the UI.

Redux, a state management library often used with React, further leverages immutability to provide predictable state updates and facilitate debugging. These examples demonstrate the tangible benefits of adopting immutability in real-world applications.

Another example is in concurrent programming. Immutable data eliminates the need for complex locking mechanisms, as there’s no risk of data races when data cannot be modified in place.

  1. Define your initial state.
  2. Create functions that take the current state and return a new state with the desired changes.
  3. Use persistent data structures to efficiently manage state updates.

Infographic Placeholder: Visual representation of how immutable data updates work with persistent data structures.

Building Robust and Maintainable Software

Ultimately, building software without mutable state leads to more robust and maintainable systems. By embracing immutability, we reduce the risk of unintended side effects, simplify debugging, and enhance the predictability of our code. While it may require a shift in mindset, the advantages of immutability are substantial and contribute to creating higher quality, more reliable software. Consider exploring functional programming principles and experimenting with immutable data structures in your next project. You might be surprised by the positive impact it has on your codebase and development process. Learn more about functional programming with resources like this guide to functional programming and this overview of immutable data structures. Check out this internal resource for more insights. This shift towards immutability, while potentially challenging, offers a rewarding path towards crafting more reliable and easier-to-maintain software systems. Embrace the change, and your code will thank you. Explore resources from authoritative sources like Wikipedia and academic institutions to deepen your understanding.

FAQ

Q: Is it possible to build any kind of application without mutable state?

A: Yes, though it might require a different approach and potentially more upfront design. The benefits in terms of code clarity and maintainability are often worth the effort.

Question & Answer :
I’ve been reading a lot of stuff about functional programming lately, and I can understand most of it, but the one thing I just can’t wrap my head around is stateless coding. It seems to me that simplifying programming by removing mutable state is like “simplifying” a car by removing the dashboard: the finished product may be simpler, but good luck making it interact with end-users.

Just about every user application I can think of involves state as a core concept. If you write a document (or a SO post), the state changes with every new input. Or if you play a video game, there are tons of state variables, beginning with the positions of all the characters, who tend to move around constantly. How can you possibly do anything useful without keeping track of changing values?

Every time I find something that discusses this issue, it’s written in really technical functional-ese that assumes a heavy FP background that I don’t have. Does anyone know a way to explain this to someone with a good, solid understanding of imperative coding but who’s a complete n00b on the functional side?

EDIT: A bunch of the replies so far seem to be trying to convince me of the advantages of immutable values. I get that part. It makes perfect sense. What I don’t understand is how you can keep track of values that have to change, and change constantly, without mutable variables.

Or if you play a video game, there are tons of state variables, beginning with the positions of all the characters, who tend to move around constantly. How can you possibly do anything useful without keeping track of changing values?

If you’re interested, here’s a series of articles which describe game programming with Erlang.

You probably won’t like this answer, but you won’t get functional program until you use it. I can post code samples and say “Here, don’t you see” – but if you don’t understand the syntax and underlying principles, then your eyes just glaze over. From your point of view, it looks as if I’m doing the same thing as an imperative language, but just setting up all kinds of boundaries to purposefully make programming more difficult. My point of view, you’re just experiencing the Blub paradox.

I was skeptical at first, but I jumped on the functional programming train a few years ago and fell in love with it. The trick with functional programming is being able to recognize patterns, particular variable assignments, and move the imperative state to the stack. A for-loop, for example, becomes recursion:

// Imperative let printTo x = for a in 1 .. x do printfn "%i" a // Recursive let printTo x = let rec loop a = if a <= x then printfn "%i" a; loop (a + 1) loop 1 

Its not very pretty, but we got the same effect with no mutation. Of course, wherever possible, we like avoid looping altogether and just abstract it away:

// Preferred let printTo x = seq { 1 .. x } |> Seq.iter (fun a -> printfn "%i" a) 

The Seq.iter method will enumerate through the collection and invoke the anonymous function for each item. Very handy :)

I know, printing numbers isn’t exactly impressive. However, we can use the same approach with games: hold all state in the stack and create a new object with our changes in the recursive call. In this way, each frame is a stateless snapshot of the game, where each frame simply creates a brand new object with the desired changes of whatever stateless objects needs updating. The pseudocode for this might be:

// imperative version pacman = new pacman(0, 0) while true if key = UP then pacman.y++ elif key = DOWN then pacman.y-- elif key = LEFT then pacman.x-- elif key = UP then pacman.x++ render(pacman) // functional version let rec loop pacman = render(pacman) let x, y = switch(key) case LEFT: pacman.x - 1, pacman.y case RIGHT: pacman.x + 1, pacman.y case UP: pacman.x, pacman.y - 1 case DOWN: pacman.x, pacman.y + 1 loop(new pacman(x, y)) 

The imperative and functional versions are identical, but the functional version clearly uses no mutable state. The functional code keeps all state is held on the stack – the nice thing about this approach is that, if something goes wrong, debugging is easy, all you need is a stack trace.

This scales up to any number of objects in the game, because all objects (or collections of related objects) can be rendered in their own thread.

Just about every user application I can think of involves state as a core concept.

In functional languages, rather than mutating the state of objects, we simply return a new object with the changes we want. Its more efficient than it sounds. Data structures, for example, are very easy to represent as immutable data structures. Stacks, for example, are notoriously easy to implement:

using System; namespace ConsoleApplication1 { static class Stack { public static Stack<T> Cons<T>(T hd, Stack<T> tl) { return new Stack<T>(hd, tl); } public static Stack<T> Append<T>(Stack<T> x, Stack<T> y) { return x == null ? y : Cons(x.Head, Append(x.Tail, y)); } public static void Iter<T>(Stack<T> x, Action<T> f) { if (x != null) { f(x.Head); Iter(x.Tail, f); } } } class Stack<T> { public readonly T Head; public readonly Stack<T> Tail; public Stack(T hd, Stack<T> tl) { this.Head = hd; this.Tail = tl; } } class Program { static void Main(string[] args) { Stack<int> x = Stack.Cons(1, Stack.Cons(2, Stack.Cons(3, Stack.Cons(4, null)))); Stack<int> y = Stack.Cons(5, Stack.Cons(6, Stack.Cons(7, Stack.Cons(8, null)))); Stack<int> z = Stack.Append(x, y); Stack.Iter(z, a => Console.WriteLine(a)); Console.ReadKey(true); } } } 

The code above constructs two immutable lists, appends them together to make a new list, and appends the results. No mutable state is used anywhere in the application. It looks a little bulky, but that’s only because C# is a verbose language. Here’s the equivalent program in F#:

type 'a stack = | Cons of 'a * 'a stack | Nil let rec append x y = match x with | Cons(hd, tl) -> Cons(hd, append tl y) | Nil -> y let rec iter f = function | Cons(hd, tl) -> f(hd); iter f tl | Nil -> () let x = Cons(1, Cons(2, Cons(3, Cons(4, Nil)))) let y = Cons(5, Cons(6, Cons(7, Cons(8, Nil)))) let z = append x y iter (fun a -> printfn "%i" a) z 

No mutable necessary to create and manipulate lists. Nearly all data structures can be easily converted into their functional equivalents. I wrote a page here which provides immutable implementations of stacks, queues, leftist heaps, red-black trees, lazy lists. Not a single snippet of code contains any mutable state. To “mutate” a tree, I create a brand new one with new node I want – this is very efficient because I don’t need to make a copy of every node in the tree, I can reuse the old ones in my new tree.

Using a more significant example, I also wrote this SQL parser which is totally stateless (or at least my code is stateless, I don’t know whether the underlying lexing library is stateless).

Stateless programming is just as expressive and powerful as stateful programming, it just requires a little practice to train yourself to start thinking statelessly. Of course, “stateless programming when possible, stateful programming where necessary” seems to be the motto of most impure functional languages. There’s no harm in falling back on mutables when the functional approach just isn’t as clean or efficient.