Functions as Values

Updated

July 30, 2026

Overview

In Go, functions are first-class citizens—they can be assigned to variables, passed as arguments, and returned from other functions.

Function Variables

// Assign function to variable
add := func(a, b int) int {
    return a + b
}

result := add(2, 3)  // 5

// Reassign
add = func(a, b int) int {
    return a + b + 1  // Different implementation
}

Function Types

type BinaryOp func(int, int) int

var op BinaryOp = func(a, b int) int {
    return a + b
}

Higher-Order Functions

Functions as Parameters

func apply(nums []int, fn func(int) int) []int {
    result := make([]int, len(nums))
    for i, n := range nums {
        result[i] = fn(n)
    }
    return result
}

double := func(n int) int { return n * 2 }
result := apply([]int{1, 2, 3}, double)  // [2, 4, 6]

Functions as Return Values

func multiplier(factor int) func(int) int {
    return func(n int) int {
        return n * factor
    }
}

double := multiplier(2)
triple := multiplier(3)

double(5)  // 10
triple(5)  // 15

Common Patterns

Callback Pattern

func fetchData(url string, callback func(data []byte, err error)) {
    // Async operation
    go func() {
        data, err := http.Get(url)
        callback(data, err)
    }()
}

Option Pattern

type Server struct {
    port    int
    timeout time.Duration
}

type Option func(*Server)

func WithPort(p int) Option {
    return func(s *Server) { s.port = p }
}

func WithTimeout(t time.Duration) Option {
    return func(s *Server) { s.timeout = t }
}

func NewServer(opts ...Option) *Server {
    s := &Server{port: 8080, timeout: 30 * time.Second}
    for _, opt := range opts {
        opt(s)
    }
    return s
}

srv := NewServer(WithPort(9000), WithTimeout(time.Minute))

Middleware Pattern

type Handler func(http.ResponseWriter, *http.Request)

func WithLogging(h Handler) Handler {
    return func(w http.ResponseWriter, r *http.Request) {
        log.Printf("%s %s", r.Method, r.URL)
        h(w, r)
    }
}

Anonymous Functions

// Immediate invocation (IIFE)
result := func(x int) int {
    return x * x
}(5)  // 25

// In goroutines
go func() {
    fmt.Println("async")
}()

Summary

Pattern Use Case
Function variable Store/swap implementations
Higher-order Transform, filter, reduce
Closures Capture state
Options Flexible configuration

More examples

Example: swap function variables

Save as main.go and go run . (with go mod init example if needed).

package main

import "fmt"

func add(a, b int) int { return a + b }
func mul(a, b int) int { return a * b }

func main() {
    op := add
    fmt.Println("add:", op(3, 4))
    op = mul
    fmt.Println("mul:", op(3, 4))
}

Expected:

add: 7
mul: 12

Example: closure captures state

Save as main.go and go run . (with go mod init example if needed).

package main

import "fmt"

func counter(start int) func() int {
    n := start
    return func() int {
        n++
        return n
    }
}

func main() {
    next := counter(10)
    fmt.Println(next())
    fmt.Println(next())
    fmt.Println(next())
}

Expected:

11
12
13

Runnable example

Save as main.go. Then:

go mod init example
go run .
package main

import "fmt"

type BinaryOp func(int, int) int

func apply(nums []int, fn func(int) int) []int {
    out := make([]int, len(nums))
    for i, n := range nums {
        out[i] = fn(n)
    }
    return out
}

func multiplier(factor int) func(int) int {
    return func(n int) int { return n * factor }
}

type Server struct {
    port    int
    timeout int // seconds, kept simple
}

type Option func(*Server)

func WithPort(p int) Option {
    return func(s *Server) { s.port = p }
}

func WithTimeout(sec int) Option {
    return func(s *Server) { s.timeout = sec }
}

func NewServer(opts ...Option) *Server {
    s := &Server{port: 8080, timeout: 30}
    for _, opt := range opts {
        opt(s)
    }
    return s
}

func main() {
    add := func(a, b int) int { return a + b }
    var op BinaryOp = add
    fmt.Println("BinaryOp:", op(2, 3))

    doubled := apply([]int{1, 2, 3}, multiplier(2))
    fmt.Println("map-like apply:", doubled)

    srv := NewServer(WithPort(9000), WithTimeout(60))
    fmt.Printf("server port=%d timeout=%ds\n", srv.port, srv.timeout)

    square := func(x int) int { return x * x }(5)
    fmt.Println("IIFE square:", square)
}

Expected output:

BinaryOp: 5
map-like apply: [2 4 6]
server port=9000 timeout=60s
IIFE square: 25

What to notice: Functions are values you can store, pass, and return; the option pattern is just a slice of func(*Server) applied at construction.

Try next: Add a WithDefaults option that resets port and timeout, or a compose(f, g) helper that returns func(int) int applying both.