Grokking Simplicity: Taming Complex Software with Functional Thinking by Eric Normand teaches functional patterns and abstractions that we as developers can use to write good code. Throw away the Haskell-on-a-chalkboard imagery; this isn’t like most functional programming (FP) books. The FP topics covered in this book are practical and will improve your every day code. No monads or applicatives here, instead you’ll learn concepts you can apply in any programming language and framework.
In the Beginning there was Data (and Actions, and Calculations)
The book begins by introducing the three basic building blocks of a program:
- Data: Primitive values, collections, and maps.
- Calculations: Pure, deterministic functions. eg.
- Actions: Impure functions where the output depends on when they were called. eg.
Software we write contains all three. Calculations are easy to reason with and unit test, we want lots of these. Actions are more difficult to test but are necessary as the software we write often connects to networks, files, and databases. Data is fed into and read from Calculations and Actions. If we’re not careful data can be accidentally changed by our Actions, so it’s important we use immutable data structures. The first third of the book gives a gentle introduction to these topics.
forEach. Throughout the book we see situations where these tools are useful and implement them ourselves.
The case study is light hearted and fits the book well. It provides a fictional and self aware narrative of the development team and stakeholders involved in the e-commerce system. This narrative and the accompanying cartoon illustrations breaks up the wall of text and provides an alternative way of understanding the books content. It’s no gimmick, but you can understand the technical content without paying attention to it.
filter, as well as a generic logging wrapper.
The section wraps up by looking at functional pipelines; a technique of organising our program so it’s mainly made up of chained functions. This pattern is popular in other languages with Java Streams, .NET LINQ, and F# pipelines.
Functional Architecture and Design Patterns
Writing low-level code in a functional style is one thing but at some point we need to assemble it into a larger program. We know architecture directly contributes to the maintainability of code but how do we do it in a functional style?
Building on top of the foundations of Data, Calculations, and Actions Norman introduces us to Stratified Design: a technique to build software in layers. Over sixty pages this idea is looked at from the angles of simplicity, abstractions, interfaces, and the resulting call graph of the program. These abstract concepts are well backed by code samples from the case study showing the before and after of the refactor. None of these are treated as solid rules and we, the reader, are asked whether applying a particular refactor has actually improved the code.
The Onion Architecture is also discussed. This architecture pattern (aka Functional Core, Imperative Shell) sits over top of Stratified Design. Code is organised into layers. The outer layer deals with the outside world (databases, networks, files) whereas the inner layers are more concerned with domain logic and pure calculations. Layers call inward, and never need to be aware of what is calling them.
I found the treatment of this section brilliant. Normand’s software development experience really shines through. Similar to the rest of the book emphasis is put on the way we think about software design and the anti-patterns to recognise, rather than a set of hard and fast rules that we’d need to stick to no matter what.
The topics I’ve talked about so far are practical for any software project. The book touches on a few other subjects which may not be applicable to every codebase but all the same are interesting. These include:
- A recursive pattern for updating nested data structures
- Timelines and Race Conditions
- Reactive programming
When dealing with these in the real world we’d reach for a library but the book implements them from scratch. These otherwise somewhat complicated topics are made accessible. It builds on the functional patterns we’ve learned in the book so far and reinforces that knowledge.
Grokking Simplicity is a perfect introduction to practical functional programming. It’s a book I wish I’d read early on in my career and I’ve already recommended it to a few colleagues. As someone experienced with the topics discussed in the book I still learned a whole lot. Normand has a great way with words and his discussions as to why these concepts make code good have stuck with me. It’s also made me appreciate design decisions of languages like Clojure and F#. I learned these languages when I was young without having faced the real-world problems such as concurrency and operations on data structures these languages do so well to solve.
Read more at: grokkingsimplicity.com.