Basics of first-class functions in Go
Tags: go, functional, tutorial, codenewbie
Published: 2021-06-02 04:18:46

---
title: Basics of first-class functions in Go
published: true
description: 
tags: #go, #functional, #tutorial #codenewbie
---

Even though Go is not mainly a functional language, it does have the core concept that **functions are first-class values**. And that comes in handy as a tool in your toolbelt to **reuse and compose code**. So let's take a look at what first-class functions in Go give you. In this tutorial, you will learn:

* 💭 What does this idea that functions are first-class values mean?
* 📦 How functions can take in other functions
* ✨ How you can use functions to build new functions
* 🐹 A practical use of first-class functions in the standard library

## 💭 Functions are first-class values, but what does that mean?

To see what first-class Go functions are, let's take a look at a simple piece of Go code with a first-class value we've seen a lot of, a plain old int.

```go
v := 250
fmt.Printf("We got value %v which is of type %T\n", v, v)
```

If you run it, you'll see this output:

```
We got value 250 which is of type int
```

The variable `v` is an `int`. And an int is something you can:
* pass into functions
* return a value of that type from functions
* and assign a value of that type to variables

Now let's see what would happen if instead, we made a variable that's a function:

```go
dbl := func(n int) int { return 2*n }
fmt.Printf("We got value %v which is of type %T\n", dbl, dbl)
```

If you run this code, you'll see output like this:

```
We got value 0x10a46a0 which is of type func(int) int
```

And what happens is:
* In the first statement, we make a new variable called `dbl`, and its value is the function that doubles an integer.
* In the second statement we pass `dbl` into a function, `Printf`. And as we can see with the message we output, `dbl`'s type in Go is `func(int) int`.

Just like an int, we can pass a `func(int) int` into a function, `Printf`. And we can do that, assign it to a variable, and return it from a function. The fact we can do all those things with a function is what it means that functions are first-class.

So now that we know we can assign a function to a variable, what exactly can we do with that?

## 📦 Passing functions into other functions

The big benefit of being able to pass a function into another function is that you can make some code in one function that manages the core logic of a piece of functionality, and then plug in whatever functions you want to handle **customized logic**.

For a fun example of passing functions to another function, let's say you got a new slushie machine. What a slushie machine does is:

* grind up some ice
* mix in syrups for the flavors you want
* and finally, give you your drink

The core logic is grinding the ice and giving you your slushie, but you can make many kinds of slushies. Berry, orange, grape, candy corn, lime. We don't want a bunch of duplicate functions like `berrySlushie()`, `limeSlushie()`, etc. And I don't know about you, but when I see a slushie machine I like to make a stack of many layers of different flavors!

Luckily, functions are first-class values and slushies are first-class beverages, so we only need one `slushie` function!

```go
func slushie(flavors ...func()) {
    fmt.Println("grinding some ice")
    for _, flavor := range flavors {
        flavor()
    }
    fmt.Println("serving your slushie")
}
```

We're passing in a variadic set of functions, one for each flavor. Grinding the ice and serving the cup always happen, but which `flavors` you pour in the cup depend on what functions you pass in. So using the `slushie` function looks like this:

```go
func addOrange() { fmt.Println("adding orange slush") }
func addBlueRasp() {
    fmt.Println("adding blue raspberry slush")
}

slushie(addBlueRasp, addOrange)
```

We made a slushie by passing two functions into our function! And if we wanted say, a lime slushie, we would just do `slushie(addLime)`. 🥤

## ✨ Functions returning functions

Not only can you pass a function into another function, first-class functions also mean you can make **functions that return functions** too!

Let's say our slushie machine was pre-programmable, so you could have your preferred stack of slushie flavors on speed dial. That would look like this in Go:

```go
func slushieSpeedDial(flavors ...func()) func() {
    yourSlushieFunc := func() { slushie(flavors...) }
    return yourSlushieFunc
}
```

Here's what happens in the code:

1. In the function signature, we're taking in a variadic set of functions that take in no arguments and return nothing. And we're returning a function as our return type.
2. We define a function function that, when called, calls `slushie` with all our flavors.
3. We return that function. We don't call that function, `slushieSpeedDial` just returns the function for you to then call elsewhere in your code.

Using our `slushieSpeedDial` function would look like this:

```go
blueAndOrangeSlushie := slushieSpeedDial(addBlueRasp, addOrange)
citrusSlushie := slushieSpeedDial(addOrange, addLime)

blueAndOrangeSlushie()
citrusSlushie()
```

## 🐹 A practical use of first-class functions in the Go standard library

The slushie machine is a fun toy example, but first-class functions also are very practical in code you actually would write for real production use cases. Which is why the Go standard library makes plenty of use of them! So here's a closer look at one of them: `sort.Slice`.

In the Go standard library, there's a convenient `sort` package, which has logic for sorting collections of data. And when that collection is a slice, the package has a `Slice` function you can use for defining the order you want the items sorted in.

Its function signature looks like this:

```go
func Slice(
    x interface{},
    less func(i, j int) bool,
)
```

The first argument is the slice we're sorting, and the second argument is a function that provides the rules for whether the i-th item in the slice should come before or after the j-th item when the slice is sorted.

In that function we pass in, if the i-th element of the slice should come before the j-th element, we return true in the `less` function. Otherwise, we have the `less` function return false. Behind the scenes, the `sort.Slice` function calls the `less` function we pass in every time we compare two items in the slice.

Let's say we wanted to sort our strings by length so shorter strings come first. We can do that by passing a function into the second parameter of `sort.Slice` that defines that rule:

```go
langs := []string{"ruby", "haskell", "go"}

sort.Slice(langs, func(i, j int) bool {
    return len(langs[i]) < len(langs[j])
})
fmt.Println(langs)
```

If we run this code, the sorted list is now in the order `go, ruby, haskell`.

Not only did we make convenient use of first-class functions to sort our slice, but we didn't even bother assigning that function to a variable. We defined the function right where it gets passed into `sort.Slice`! That technique is called **anonymous functions**, or **lambda functions**.

If we instead wanted to sort alphabetically, we would just need to change the function you're passing in.

```go
sort.Slice(langs, func(i, j int) bool {
    return langs[i] < langs[j]
})
```

Run that, and the list of languages would be in the order `go, haskell, ruby`.

Notice how we're using the same function, `sort.Slice`, both times. `sort.Slice` handles the common infrastructure to the sorting, like managing the sorting algorithm we're running. Then we can plug in any `func (i, j int) bool` we want for managing the logic of whether one element of the slice should come before another.

When a function takes in other functions, it's called a **higher-order function**. And as we saw, we don't need to think about whether the `sort.Slice` higher-order function is running quicksort, heapsort, or bubblesort. We don't need to write all the code for swapping items in the slice. All we had to write our own code for is **which order** we want the items to go in! So when you have functionality that is repeating mostly the same logic except one part, either a higher-order function or a function that takes in an interface can come in handy for centralizing that shared logic.