Home Programming Functional Programming in Go

Functional Programming in Go

By Dylan Meeus
ai-assist-svg-icon Book + AI Assistant
eBook + AI Assistant $31.99 $21.99
Print $39.99
Subscription $15.99 $10 p/m for three months
ai-assist-svg-icon NEW: AI Assistant (beta) Available with eBook, Print, and Subscription.
ai-assist-svg-icon NEW: AI Assistant (beta) Available with eBook, Print, and Subscription. $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime! ai-assist-svg-icon NEW: AI Assistant (beta) Available with eBook, Print, and Subscription.
What do you get with a Packt Subscription?
Gain access to our AI Assistant (beta) for an exclusive selection of 500 books, available during your subscription period. Enjoy a personalized, interactive, and narrative experience to engage with the book content on a deeper level.
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
Gain access to our AI Assistant (beta) for an exclusive selection of 500 books, available during your subscription period. Enjoy a personalized, interactive, and narrative experience to engage with the book content on a deeper level.
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Along with your eBook purchase, enjoy AI Assistant (beta) access in our online reader for a personalized, interactive reading experience.
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
ai-assist-svg-icon NEW: AI Assistant (beta) Available with eBook, Print, and Subscription. ai-assist-svg-icon NEW: AI Assistant (beta) Available with eBook, Print, and Subscription. BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime! ai-assist-svg-icon NEW: AI Assistant (beta) Available with eBook, Print, and Subscription.
eBook + AI Assistant $31.99 $21.99
Print $39.99
Subscription $15.99 $10 p/m for three months
What do you get with a Packt Subscription?
Gain access to our AI Assistant (beta) for an exclusive selection of 500 books, available during your subscription period. Enjoy a personalized, interactive, and narrative experience to engage with the book content on a deeper level.
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
Gain access to our AI Assistant (beta) for an exclusive selection of 500 books, available during your subscription period. Enjoy a personalized, interactive, and narrative experience to engage with the book content on a deeper level.
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Along with your eBook purchase, enjoy AI Assistant (beta) access in our online reader for a personalized, interactive reading experience.
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    Chapter 1: Introducing Functional Programming
About this book
While Go is a multi-paradigm language that gives you the option to choose whichever paradigm works best for the particular problem you aim to solve, it supports features that enable you to apply functional principles in your code. In this book, you’ll learn about concepts central to the functional programming paradigm and how and when to apply functional programming techniques in Go. Starting with the basic concepts of functional programming, this Golang book will help you develop a deeper understanding of first-class functions. In the subsequent chapters, you’ll gain a more comprehensive view of the techniques and methods used in functional languages, such as function currying, partial application, and higher-order functions. You’ll then be able to apply functional design patterns for solving common programming challenges and explore how to apply concurrency mechanisms to functional programming. By the end of this book, you’ll be ready to improve your code bases by applying functional programming techniques in Go to write cleaner, safer, and bug-free code.
Publication date:
March 2023
Publisher
Packt
Pages
248
ISBN
9781801811163

 

Introducing Functional Programming

In this first chapter, we are going to take a bird’s eye view of the what and why behind functional programming (FP). Before we dive into the nitty gritty of FP, we first have to understand what benefit we get from applying these techniques to our code. To start off, we will provide a brief look into the history and contemporary state of FP methodologies. Next, we will take a look at how FP compares to more traditional object-oriented programming (OOP). Finally, we will also discuss the “Go programming paradigm.”

The main things we will cover in this chapter are as follows:

  • What is FP?
  • A brief history of FP
  • A look at the current state of FP
  • A comparison of traditional object-oriented and functional methodologies
  • A discussion on Go programming paradigms and how FP fits into this
 

What is functional programming?

As you might have guessed, FP is a programming paradigm where functions play the main role. Functions will be the bread and butter of the functional programmer’s toolbox. Our programs will be composed of functions, chained together in various ways to perform ever more complex tasks. These functions tend to be small and modular.

This is in contrast with OOP, where objects play the main role. Functions are also used in OOP, but their use is usually to change the state of an object. They are typically tied to an object as well. This gives the familiar call pattern of someObject.doSomething(). Functions in these languages are treated as secondary citizens; they are used to serve an object’s functionality rather than being used for the function itself.

Introducing first-class functions

In FP, functions are considered first-class citizens. This means they are treated in a similar way to how objects are treated in a traditional object-oriented language. Functions can be bound to variable names, they can be passed to other functions, or even served as the return value of a function. In essence, functions are treated as any other “type” would be. This equivalence between types and functions is where the power of FP stems from. As we will see in later chapters, treating functions as first-class citizens opens a wide door of possibilities for how to structure programs.

Let’s take a look at an example of treating functions as first-class citizens. Don’t worry if what’s happening here is not entirely clear yet; we’ll have a full chapter dedicated to this later in the book:

package main
import “fmt”
type predicate func(int) bool
func main() {
    is := []int{1, 1, 2, 3, 5, 8, 13}
    larger := filter(is, largerThan5)
    fmt.Printf(“%v”, larger)
}
func filter(is []int, condition predicate) []int {
    out := []int{}
    for _, i := range is {
        if condition(i) {
            out = append(out, i)
        }
    }
    return out
}
func largerThan5(i int) bool {
    return i > 5
}

Let’s break what’s happening here down a bit. First, we are using a “type alias” to define a new type. The new type is actually a “function” and not a primitive or a struct:

type predicate func(int) bool

This tells us that everywhere in our code base where we find the predicate type, it expects to see a function that takes an int and returns a bool. In our filter function, we are using this to say we expect a slice of integers as input, as well as a function that matches the predicate type:

func filter(is []int, condition predicate) []int {…}

These are two examples of how functions are treated differently in functional languages from in object-oriented languages. First, types can be defined as functions instead of just classes or primitives. Second, we can pass any function that satisfies our type signature to the filter function.

In the main function, we are showing an example of passing the isLargerThan5 function to the filter function, similar to how you’d pass around objects in an object-oriented language:

larger := filter(is, largerThan5)

This is a small example of what we can do with FP. This basic idea, of treating functions as just another type in our system that can be used in the same way as a struct, will lead to the powerful techniques that we explore in this book.

What are pure functions?

FP is often thought of as a purely academic paradigm, with little to no application in industry. This, I think, stems from an idea that FP is somehow more complicated and less mature for the industry than OOP. While the roots of FP are academic, the concepts that are central to these languages can be applied to many problems that we solve in industry.

Often, FP is thought of as more complex than traditional OOP. I believe this is a misconception. Often, when people say FP, what they really mean to say is pure FP. A pure functional program is a subset of FP, where each function has to be pure – it cannot mutate the state of a system or produce any side effects. Hence, a pure function is completely predictable. Given the same set of inputs, it will always produce the same set of outputs. Our program becomes entirely deterministic.

This book will focus on FP without treating it as the stricter subset of “pure” FP. That is not to say that purity brings us no value. In a purely functional language, functions are entirely deterministic and the state of a system is unchanged by calling them. This makes code easier to debug and comprehend and improves testability. Chapter 6 is dedicated to function purity, as it can bring immense value to our programs. However, eradicating all side effects from our code base is often more trouble than it’s worth. The goal of this book is to help you write code in a way that improves readability, and as such, we’ll often have to make a trade-off between the (pure) functional style and a more forgiving style of FP.

To briefly and rather abstractly show what function purity is, consider the following example. Say we have a struct of the Person type, with a Name field. We can create a function to change the name of the person, such as changeName. There are two ways to implement this:

  • We can create a function that takes in the object, changes the content of the name field to the new name, and returns nothing.
  • We can create a function that takes in an object and returns a new object with the changes applied. The original object is not changed.

The first way does not create a pure function, as it has changed the state of our system. If we want to avoid this, we can instead create a changeName function that returns a new Person object that has identical field values for each field as the original Person object does, but instead has a new name in the name field. The diagram here shows this a bit more abstractly:

Figure 1.1: Pure function (top) compared to impure function (bottom)

Figure 1.1: Pure function (top) compared to impure function (bottom)

In the top diagram, we have a function (denoted with the Lambda symbol) that takes a certain object, A, as input. It performs an operation on this object, but instead of changing the object, it returns a new object, B, which has the transformation applied to it. The bottom diagram shows what was explained in the earlier paragraph. The function takes object A, makes a change “in-place” on the object’s values, and returns nothing. It has only changed the state of the system.

Let’s take a look at what this would look like in code. We start off by defining our struct, Person:

type Person struct {
    Age  int
    Name string
}

To implement the function that mutates the Person object and places a new value in the Name field, we can write the following:

func changeName(p *Person, newName string) {
    p.Name = newName
}

This is equivalent to the bottom of the diagram; the Person object that was passed to the function is mutated. The state of our system is now different from before the function was called. Every place that refers to that Person object will now see the new name instead of the old name.

If we were to write this in a pure function, we’d get the following:

func changeNamePure(p Person, newName string) Person {
    return Person{
        Age:  p.Age,
        Name: newName,
    }
}

In this second function, we copy over the Age value from the original Person object (p) and place the newName value in the Name field. The result of this is returned as a new object.  

While it’s true that the former, impure way of writing code seems easier superficially and takes less effort, the implications for maintaining a system where functions can change the state of the system are vast. In larger applications, maintaining a clear understanding of the state of your system will help you debug and replicate errors more easily.

This example looks at pure functions in the context of immutable data structures. A pure function will not mutate the state of our system and always return the same output given the same input.

In this book, we will focus on the essence of FP and how we can apply the techniques in Go to create more readable, maintainable, and testable code. We will look at the core building blocks, such as higher-order functions, function currying, recursion, and declarative programming. As mentioned previously, FP is not equivalent to “pure” FP, but we will discuss the purity aspect as well.

Say what you want, not how you want it    

One commonality that is shared between FP languages is that functions are declarative rather than imperative. In a functional language, you, as the programmer, say what you want to achieve rather than how to achieve it. Compare these two snippets of the Go code.

The first snippet here is an example of valid Go code where the result is obtained declaratively:

func DeclarativeFunction() int {
    return IntRange(-10,10).
        Abs().
        Filter(func(i int64) bool {
            return i % 2 == 0
        }).
        Sum()
    // result = 60 
}

Notice how, in this code, we say the following things:

  • Give us a range of integers, between -10 and 10
  • Turn these numbers into their absolute value
  • Filter for all the even numbers
  • Give us the sum of these even numbers

Nowhere did we say how to achieve these things. In an imperative style, the code would look like the following:

func iterativeFunction() int {     
    sum := 0
    for i := -10; i <= 10; i++ {
        absolute := int(math.Abs(float64(i)))
        if absolute%2 == 0 {
            sum += absolute
        }
    }    
    return sum
}

While, in this example, both snippets are easy to read for anyone with some Go experience, we can imagine how this would stop being the case for larger examples. In the imperative example, we have to spell out literally how the computer is supposed to give us a result.

 

A brief history of functional programming

If you take a look at the mainstream languages of the past decade, you will notice how the prevailing programming paradigm is OOP. This might lead you to believe that FP is an upstart paradigm, one that is in a young state compared to the well-established object-oriented approach. Yet, when we look at the history of FP, we can trace its roots all the way back to the 1930s, quite some time before we talked about programming in the modern-day sense.

The roots of FP can be traced back to the Lambda calculus, which was developed in the 1930s by Alonzo Church. This was developed as a formal system based on function abstraction and application, using variable binding. There are two variants of this calculus; it can either be typed or untyped. This is directly parallel to how programming languages today are either statically typed, such as Java and Go, or dynamically typed such as Python. The Lambda calculus was proven to be Turing-complete in 1937 – again, similar to how all mainstream programming languages today are Turing-complete.

The Lambda calculus predates modern programming by a few decades. To get to the first actual code that could be thought of as FP, in the way that we understand programming today, we have to move forward a few decades. The LISt Processor (LISP) was originally created in the 1950s as a practical application of mathematical notation. This was influenced by the Lambda calculus laid out in the 1930s by Church.

LISP can be thought of as the first FP language that reached some sense of popularity. It was especially popular in the field of artificial intelligence research, but through the decades, made its way to industry. Derivatives of LISP continued to be popular for a long time, with notable achievements such as the Crash Bandicoot game, and Hacker News being written in derivatives of this language.

LISP was developed in the late 1950s by John McCarthy. To define LISP functions, he took inspiration from the Lambda calculus developed by Church. It was extended beyond the mathematical system by introducing recursion, a fundamental concept for how functional languages work. Beyond recursion, LISP also treated functions as first-class citizens and pushed innovation in programming language design by including things such as garbage collection and conditional statements.  

In the early 1960s, Kenneth E. Iverson developed A Programming Language (APL). APL is, again, a functional language that is perhaps most known for its use of symbols and terse code.  For example, the following is an image of the code snippet that would generate Conway’s Game Of Life:

Figure 1.2: Conway’s Game of Life in APL

Figure 1.2: Conway’s Game of Life in APL

Jumping ahead another decade, in 1973, we get a language called Meta Language (ML). This language introduced the polymorphic Hindley-Milner type system – that is to say, a type system in which types are assigned automatically without requiring explicit type annotations. In addition, it supported features such as function currying, which we will apply to our functional Go code later in this book. It also supports pattern matching on the arguments of a function, as we can see in the following snippet of a function to compute the factorial of a number:

fun fac 0 = 1
  | fac n = n * fac (n – 1)

The pattern matcher in this example will take a look at what the input value is to the fac function, and then either continue with the first line if the input value is 0, or the second line in all other cases. Notice that this is also a recursive function expressed quite beautifully. Sadly, pattern matching will not be explored further in this book, as Go currently offers no way of doing this. We will see a way of doing a similar type of function dispatching using maps and higher-order functions.

In 1977, the language called FP was created. It was developed by John Backus specifically to support the FP paradigm. While the language itself did not get much traction outside of academia, the paper in which it was introduced (Can programming be liberated from the von Neumann style?) did spark a renewed interest in FP.

In the same decade as ML and FP, another language called Scheme was developed. This is the first dialect of LISP that used lexical scoping and tail-call optimization. Tail-call optimization led to the practical implementation of recursive algorithms. While the details of tail-call optimization will be discussed in Chapter 7 of this book, briefly stated, it allows recursive algorithms to be implemented in an efficient way and without using more memory than a traditional loop would, thus eliminating the “stack overflow exceptions” that otherwise would happen during deep recursion.

Scheme is one of the most influential LISP dialects to have been created, and continues to this day to have some popularity. Although it was created in 1975, the last standard was defined as recently as 2013 (R7RS-Small). Scheme, in turn, influenced other LISP dialects, the most notable of which is perhaps Common Lisp. Interestingly, although having roots in FP, Common Lisp introduced the Common Lisp Object System (CLOS). The CLOS facilitated OOP in LISP. With this, we can perhaps consider LISP a truly multi-paradigm language, not unlike Go.

The final language to look at before we jump to contemporary functional languages is Miranda. Miranda is a lazy, purely FP language. The key concept that was introduced here is lazy evaluation. When a language is said to support lazy evaluation, this means that an expression is not resolved until the value is actually needed. It can be used, for example, to implement infinite data structures. You could define a function that generates all Fibonacci numbers, a sequence that never ends – but, rather than creating the entire list (which is not possible), it will only generate a subset of that list that is relevant to the problem you are solving. For example, the following snippet of Miranda code computes all square numbers:

squares = [ n*n | n <- [0..] ]

With that, we have arrived at the next language to discuss briefly, namely Haskell.

 

Modern functional programming

After our brief look at the history of FP, it’s time to dive into modern functional languages. One of the languages that is popular today within the strict FP languages is Haskell. When people study FP or become exposed to it, it is often through this language. Haskell is a statically typed FP language. It has goodies such as type inference (like ML) and lazy evaluation (like Miranda).

When people want to learn more about pure FP, my recommendation is always to start with Haskell. It has a great community and plenty of resources and teaches you all there is to know about the FP domain.

It might very well be the most popular pure FP language around, yet it accounts for less than 1% of the active users on GitHub (https://www.benfrederickson.com/ranking-programming-languages-by-github-users/). For fun, if we take a look at Go, it currently sits at about approximately 4% of active users. Not bad for a language that’s just about a decade old at this point!

In the .NET world, another language that is relatively popular is F#. While this is not a purely functional language such as Haskell, it is a functional-first language. It prefers the functional paradigm over others but does not enforce it. Similarly to Haskell, it has less than 1% of active users on GitHub. C# seems to get all the popular features of F# though, so at least the functional concepts that F# spearheaded for .NET will find popularity.

So does that mean functional programming is dead on arrival? Well, not quite. The book you are reading now is all about Go, and Go is not a purely FP language. My take on it is that the concepts from FP are generally useful and can create better code, even in object-oriented languages – and I’d like to think I’m not alone in thinking this. Many of the languages that we think of as object-oriented languages have become more and more functional.

We can see this shift happening even in the most popular mainstream object-oriented languages. Java is introducing FP concepts with each iteration, offering such things as pattern matching, higher-order functions, and declarative programming through Lambda functions. C# is looking more and more like F# (Microsoft’s functional programming counterpart of C#) with each release. They have implemented pattern matching, immutability, built-in tuple support, and more.

This shift is happening because, although purely functional languages might not always suit the industry, the concepts from functional languages allow us to write our object-oriented code with more confidence. They lead to code that is easier to test, easier to read, and faster to debug.

The most popular programming language used today is JavaScript. While this would perhaps not pop into people’s minds when talking about FP, it does meet a subset of the “requirements” we have for a functional language. It has the following:

  • First-class functions
  • Anonymous (Lambda) functions
  • Closures

When combining these features, we can create many constructs that allow us to leverage code in an FP style.

For those of us who want to have a purely functional language in the browser, there are languages that transpile to JavaScript, such as Elm and PureScript.

Let’s now take a look at the star of this book, Go, and how this fits into the picture.

 

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.

 

Why functional programming?

All this does not yet tell us why we want to invest time in learning about FP. The main benefits we hope to get from functional programming are as follows:

  • More readable code
  • Easier to understand and debug code
  • Easier testing
  • Fewer bugs
  • Easier concurrency

These can be achieved by a relatively small set of FP features. To achieve more readable code, this can be done by writing code in a declarative way. Declarative programming will show us what is happening rather than how it is happening. Declarative code is often more concise than the imperative counterpart. Conciseness is not necessarily a benefit to code readability (remember the APL example previously?) but when applied correctly, it can be.

FP makes code easier to understand, debug, and test by preferring purity over impurity. When each function always creates a deterministic outcome, we can trust a function does only what it says. When you encounter a function called square(n int), we can be convinced that all the function does is square the input.

In addition, the state of the system is not changed. If we are working with structs and objects, it helps us guarantee that the values our object holds are not changed by functions that are operating on it. This reduces the cognitive overhead when reasoning about our programs.

Pure, immutable code makes code easier to test for the following reasons:

  • The state of the system has no impact on our function – so we don’t have to mock the state when testing.
  • A given function always returns the same output for a given input. This means we get predictable, deterministic functions.

I won’t be advocating for test-driven development or any such thing here, but I do believe testing is critical to writing good code. Or at least, to avoid being paged at 3 A.M. because a function started throwing unintelligible error codes at a user.

Hand in hand with more testable code, FP helps us write fewer bugs. This is perhaps hard to quantify, but the idea here is that without mutable states and with only predictable functions in our code, we’ll have fewer edge cases to think about. If the state is important to your program, you have to know, at each point in time, what the state of the system can be and how it influences the function you are writing. This gets complex fast.

Finally, FP will make writing concurrent code easier. Go is a pretty well-known language for its built-in concurrency features. Concurrency was part of Go from its inception and was not tacked on later as with some other mainstream languages. As a result, Go has pretty solid concurrent coding tools.

The way in which functional programming helps is that functions are deterministic and immutable. Thus, running the same function concurrently can never impact the result of another running function. If functions never depend on the state of the system, thread A can not invalidate the system’s state for thread B.

One thing I want to highlight again, as it is important, is that I don’t advocate sticking to pure FP in Go. Doing so will probably make your life, and that of your coworkers, harder than it has to be. Choose the right tool for the job – sometimes that will be objects, and sometimes that will be functions.

 

Why not functional programming in Go?

To provide a holistic view of how FP can help us, as Go programmers, we should also consider when not to use FP. I view FP as a tool in my toolbox and when a problem lends itself to it, I will gladly use it – but just as importantly, we have to recognize when this does not work.

One of the concerns around FP is performance – while there is a lot to say on this topic, as we’ll see in later chapters, performance concerns could mean we throw out some functional concepts such as immutability in favor of executing with speed. This is more complex than it might sound at first, as Go pointers are not guaranteed to be faster than Go’s pass-by-value functions. We’ll expand more on the performance concerns in later chapters.

Another reason not to choose FP is Go’s lack of tail-call optimization. In theory, every loop you write in your program could be replaced by a recursive call, but as of Go 1.18, Go does not have the necessary tools to do this efficiently and you’d risk running into stack overflows. There are ways around this, as we will see, but if it starts sacrificing performance or readability significantly, my advice would be to just write a loop. This is not to say recursion is never the right approach. If you’ve worked with trees or graphs extensively, you’ve probably written some recursive algorithms and found them to work just fine.

Finally, if you are working on an existing code base with many other contributors, the best thing to do is follow the style of the code base. While some concepts of FP can be introduced quite easily, it is harder to enforce them in a team that’s not on board with the whole idea. Luckily, many programmers today see benefits in key concepts of FP. Even in Java or C#, the idea of immutable code is embraced. Side effects similarly are more and more seen as unwanted.

Let’s embrace Go as a fully multi-paradigm language and leverage each paradigm where it makes sense.

 

Comparing FP and OOP

As we have seen in the preceding pages, FP is not exactly a new thing. It, in fact, predates the object-oriented paradigm by a few decades. While Go is multi-paradigm and we can embrace both styles of programming, let’s take a quick look at a concrete comparison between the two.

Functional programming

Object-oriented programming

Functions are the bread and butter

Classes and objects are the bread and butter

Declarative code

Imperative code

Immutability preferred

Mutable state

Can enforce purity

Often no focus on purity

Recursion

Loops

Table 1.1: Table comparing FP (left) and OOP (right)

This comparison is a tad superficial. Many object-oriented languages also have a notion of recursion, but it’s not always central to the language’s design. Similarly, object-oriented code can encapsulate the mutable state and try to get immutability as much as possible.

In today’s world, even languages that we consider traditionally object-oriented, such as Java, are, in fact, becoming more and more multi-paradigm.

As a side note, this comparison might make it seem like there are only three possible paradigms: functional, object-oriented, or multi-paradigm. While these are certainly the most common, there are other paradigms, such as literate programming, logic programming, and reactive programming. As OOP is the main player in this space, and thus what most readers are familiar with, that will be a focus of comparison throughout this book.

 

Summary

As we have seen in this first chapter, FP is not exactly the “new kid on the block.” It is a paradigm that stems from the work of Alonzo Church in the 1930s. It has seen continuous and steady investment since the 1950s with various languages pushing the paradigm further and further.

As we have also seen, FP and OOP are being combined more and more in modern languages, with Java and C# integrating ideas from the functional paradigm into their object-oriented paradigm. Go, the star of this book, takes this a step further and is a multi-paradigm language. Go gives us complete freedom to write code in whichever domain suits us best.

The core idea to remember from this chapter is that the FP paradigm will help us write code that is easier to test, read, and maintain. It reduces cognitive overhead by limiting side effects, not mutating the state of our system, and favoring small composable functions.

Finally, it is also important to remember that, although we advocate for the FP paradigm in this book, Go is multi-paradigm, and we have to choose the right paradigm for the problem we are solving.

About the Author
  • Dylan Meeus

    Dylan Meeus is a Software Engineer, with over a decade of experience in various functional and non-functional programming languages. He has used Go to develop systems in a variety of domains, from healthcare to frameworks for machine learning platforms and digital signal processing software. He developed a passion for Functional Programming when learning Haskell and applied this knowledge to traditionally non-functional languages like Java. Over the past several years, Dylan has been a speaker at various Go and Java-oriented conferences such as GopherCon and Devoxx.

    Browse publications by this author
Functional Programming in Go
Unlock this book and the full library FREE for 7 days
Start now