Functional Effectsby on
We recently had a discussion about side effects in functional programming over lunch. The question arose, if it is possible to write software with real business value purely functional — so without any side effects at all. Diving deeper into side effects reveals that it actually is possible, for the biggest part of your software.
But before we can take up the challenge we should define what we do not want.
I define a side effect as interactions with the real world. Side effects are manipulations of physical structures, e.g. turning bits on a hard drive, changing state of the screen or boot a nuclear reactor. Side effects are considered /impure/ in a functional programming sense, since they break the rules of totality and pureness: A pure function is one that takes an input and returns an output and will do that for all inputs deterministically. A total function has all is one that strictly maps one set of inputs onto a second set of outputs. A function with side effects is neither pure nor total since it is all foremost non deterministic in its behaviour.
In Object Oriented Programming side effects are utilised as the very basic building blocks for software since that is the interaction with the real world.
But in (pure) functional programming there are no side effects.
“Functional effects are immutable data structures that describe side effects.” – John De Goes
Since functional programming is transformation of immutable data structures we use those structures to describe side effects. Consider baking a cake. Instead of just starting the cake baking throwing some flour in a bowl, breaking some eggs and then realising that we don’t have sugar in the house and crash; we starting with describing, what we would have to do in order to bake that cake. At the end we will have to interact with the real world — however that might look like. We could actually bake the cake or just release a cake baking book. But that happens at the very edge of our program via an interpreter for our data structure.
The interpreter is in its simplest case a run function that runs over the immutable data structure and translates every described operation into an actual side effect — outside of the program we have written. This keeps the programmer in a purely functional mind set for the entirety of the software development process.
The run function takes the function that describes the side effect and evaluates it at the appropriate time.
The Business Values - Why do we even bother?
To describe effects throughout our software allows us to sandbox the real world manipulation. That means we get composable time boxing and cancellation (for free).
With functional effects it is possible to write functions that take effects as input and return effects as values. Those functions are composable and so effects can be transformed before they interact with the real world. Since functional effects behave just like values we can do all the functional transformation on effects that we can do on values. That includes somewhat easy concurrency and parallelism.
Since it’s all pure functional programming we can easily reasoning about the type transformations along the program flow. That is not a direct benefit of functional effects, but of pure functional programming that is enabled through pushing the side effects into the interpreter of your program (the very edge of your software).
Another benefit of pure functional programming is the very good testability of the entire code base. Since all functions are pure and total, unit and integration tests are building ups confidence in the code quite easily. And the code base is ready for property based testing as well.