In this article, we will continue our investigation of applying functional programming concepts to Go. While in the previous article, we concentrated on pure functions, we will dive into the ideas and implementation behind effectful functions.
Guiding Principles
Let’s recap some of the guiding principles.
Pure functions
In functional programming, we try to isolate pure functions from effectful functions. Functions are considered pure if the output of a function depends on its inputs only and if the invocation of such a function does not change anything outside the scope of the function itself (e.g., global variables, the console, files, etc.).
Immutability
Data structures are considered immutable, i.e., instead of modifying a structure to change data, we create copies of that structure and apply the modifications to that copy.
There exist many benefits of immutability, including:
- The caller of a function does not have to worry that the function modifies its input data, so it’s safe to pass the same input to multiple functions, synchronously or asynchronously, to cache it, etc.
- Functions are easy to understand since, by definition, it is clear that inputs are always read-only and that the only output of a function is its return value. That makes these functions easy to reason about and easy to test
Composition
When designing functions, we attempt to design them so the output of one function can be passed as an input to the next function, forming a new function.
If all functions in a composition are pure, then the result of the composition is pure, too, so we can derive more complex scenarios from simpler ones.
Side Effects
While it is desirable to work with pure functions, most real-world programs need side effects because they, e.g., read files, consume user input, rely on environment variables, make HTTP requests, etc.
Side effects are operations that alter or rely on a state outside the current function's scope. Here’s some examples:
- Reading from a file because the file’s content lies outside of the control of the function. So executing a function repeatedly does not guarantee to produce the same result each time
- Reading an environment variable
- Writing to a file because the write operation might fail
- Writing to the console because doing this repeatedly will produce different results (one vs many console logs)
- Relying on the current time
In functional programming, we try to isolate side effects from pure functions such that their execution is effectful but their composition remains pure.
Representation
We represent a side effect as a function without inputs but with a strongly typed return value.