The Go programming paradigm
Unless this is your first introduction to Go, you probably know that Go is a statically typed programming language. You also know that it has structs and that we can instantiate objects out of these. You likely also know that Go optionally binds functions to a struct, but that is not required. It would be possible to write an entire Go program without creating an object, something that the stricter object-oriented languages rarely allow.
In fact, the simplest Hello World
program in Go has no sense of structs or objects:
package main import “fmt” func main() { fmt.Println(“Hello Reader!”) }
As you can see, the introductory Go program that many of us wrote when starting to learn Go has no notion of structs or objects to do something useful. Println
is a function defined in the fmt
package, but it’s not bound to an object.
The term for a language such as Go is multi-paradigm. Go does not force us to write code in the object-oriented paradigm or in the functional paradigm. We, the programmers, have complete freedom to use the language however we want. This is why the book you are reading right now exists.
Go offers several features that enable us to write functional Go code with (relative) ease:
- Functions as first-class citizens
- Higher-order functions
- Immutability guarantees
- Generics (not needed per se, but make life easier)
- Recursion
These are explored in more detail later in the book. I also want to point out some features that Go lacks (as of 1.18) that would improve our quality of life:
- Tail-call optimization
- Lazy evaluation
- Purity guarantee
These are not deal-breakers. The focus of this book is leveraging FP in Go to write better code. Even if we don’t have a purely statically typed system to work with, we can work with what we do have.
By no means do I want to posit FP as the superior way to write Go code. Nor do I want to frame it as the “right” paradigm to choose. Go is multi-paradigm, and just as programmers choose the right language for any problem, we also have to choose the right paradigm for each problem. We can even opt to stick to functional concepts 90% of the time and still end up with cleaner code than if we had stuck to it 100%. For example, writing purely functional code would prevent the use of any side effects. Yet, many side effects do serve a purpose. Any time we want to show a user output or get input from a user, we are technically dealing with a side effect.