Skip to content
Theory · Functions as values · lesson 1 of 12

Functions as values

What you'll learn

What you'll learn:

  • pass a function as an argument, store it in a variable and return it from another function;
  • explain what a closure is and why it sees the variables around it;
  • defer a call until the function exits with defer, and know the order of deferred calls;
  • stop a panic with recover.
A sorting machine SortFunc with the rule card byLen in its slot, and tiles come out from shortest to longest

Picture a recipe card. You can hand it to someone else, and they will cook the dish by your rules, though they did not invent the recipe. You can put the card in a box and take it out later. The card itself is not a dish but instructions for making one.

In Go you can hand a function to other code in the same way, like such a card. The slices.Sort function orders strings alphabetically, but sorting by length needs a different comparison rule. A rule is code, and in Go you pass it to the slices.SortFunc function as an ordinary argument. Picture slices.SortFunc as a sorting machine with a slot for a card. The words go in at the top, the rule card goes into the slot, and the machine lays the words out the way the card says.

A function as an argument

The argument of slices.SortFunc is itself a function:

GO
package main

import (
    "fmt"
    "slices"
)

func byLen(a, b string) int { return len(a) - len(b) }

func main() {
    words := []string{"kiwi", "fig", "banana"}
    slices.SortFunc(words, byLen)
    fmt.Println(words) // [fig kiwi banana]
}

Let's see what happens here:

  1. func byLen(a, b string) int compares two strings by length and returns the difference of their lengths.
  2. words := []string{"kiwi", "fig", "banana"} creates a slice of three words.
  3. slices.SortFunc(words, byLen) receives byLen and itself calls it for pairs of elements.
  4. fmt.Println(words) prints [fig kiwi banana].

The sign of the result tells slices.SortFunc how to place a pair:

  • a negative result—a must come before b;
  • a positive one—a must come after b;
  • zero—the order does not matter.

For "fig" and "kiwi", byLen gives 3 - 4, that is -1, so fig goes first.

This way the single function slices.SortFunc sorts by any rule: by length, by the last letter, in descending order. The rule changes, while the sorting code stays the same—only the card changes.

Function types

A function's name without parentheses is the function itself, not a call:

  • byLen hands over the card;
  • byLen("fig", "kiwi") cooks the dish right now and gives a number.

As a value, a function has a type. A function type is built from three parts:

  • the word func;
  • the parameter types in parentheses: (string, string);
  • the result type: int.

For byLen it is func(string, string) int. A variable can have this type:

GO
var cmp func(string, string) int
cmp = byLen
fmt.Println(cmp("fig", "kiwi")) // -1

Line by line:

  1. var cmp func(string, string) int declares a variable of a function type.
  2. cmp = byLen stores the function byLen in it.
  3. fmt.Println(cmp("fig", "kiwi")) calls the function through the variable and prints -1, the same as a call by name.

The compiler will not store a function with other parameters or another result in cmp.

First-class values

In Go you can do three things with a function:

  • assign it to a variable;
  • pass it as an argument;
  • return it from another function.

That is why functions in Go are called first-class values. Interviewers often ask what this means, and the answer is these three actions.

Comparing functions

Here the recipe-card picture ends. Two paper cards can be compared, but two functions cannot: byLen == byLen does not compile, with the error func can only be compared to nil.

A function is compared only with nil—the zero value of a variable like cmp before anything is stored in it.

A function without a name

A function can be written right inside an expression, without a name. This is called a function literal, or an anonymous function. It is built like an ordinary function declaration, except there is no name between func and the parameter list.

A literal as an argument

A literal is written right where the argument goes. The call slices.SortFunc(words, func(a, b string) int { return len(b) - len(a) }) sorts words from longest to shortest.

This is handy for a rule needed in one place of the program. There is no reason to give it a separate name.

A literal as a result

A literal is also handy for returning a function from a function:

GO
func pick(op string) func(int, int) int {
    if op == "+" {
        return func(a, b int) int { return a + b }
    }
    return func(a, b int) int { return a * b }
}

func main() {
    f := pick("*")
    fmt.Println(f(3, 4), pick("+")(3, 4)) // 12 7
}

Let's walk through the example line by line:

  1. func pick(op string) func(int, int) int declares a function whose result has the type func(int, int) int.
  2. When op == "+", pick returns the literal that adds.
  3. Otherwise it returns the literal that multiplies.
  4. f := pick("*") gets the multiplication function, so f(3, 4) gives 12.
  5. pick("+")(3, 4) first gets the addition function and then calls it right away with 3 and 4—that makes 7.

Common mistake

The result of a call is passed instead of the function:

GO
slices.SortFunc(words, byLen()) // ✗
TEXT
not enough arguments in call to byLen

The parentheses after byLen call the function right on this line, and there are no arguments for the call. SortFunc needs the card itself so it can call it for each pair. Pass the name without parentheses: slices.SortFunc(words, byLen).

Key points

  • A function in Go is a value: you assign it to a variable, pass it as an argument and return it from a function.
  • A function type is written with func, the parameter types and the result type: func(string, string) int.
  • A name without parentheses passes the function itself; parentheses call it on the spot.
  • A function literal is a function without a name, written right inside an expression.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way