Can Go’s New Generics Unlock Functional Programming?

This article explores how Go 1.18’s generic type parameters enable functional programming features such as higher‑order functions, lazy evaluation, and partial application, while also highlighting the limitations and design compromises that arise from Go’s type system and language ergonomics.

MaGe Linux Operations
MaGe Linux Operations
MaGe Linux Operations
Can Go’s New Generics Unlock Functional Programming?

Background

Functional programming (FP) offers statelessness, no side effects, concurrency friendliness, and high abstraction, and many popular languages (C++, Python, Rust) have adopted FP features. Go, however, has seen little discussion because of the lack of generic support, which made writing type‑agnostic functions difficult.

Recent proposals (e.g., spec: add generic programming using type parameters #43651 ) have been accepted, and Go 1.18 (early 2022) introduced generics, prompting a re‑examination of FP in Go.

Overview

This article incrementally implements common FP features using Go generics to evaluate their advantages and shortcomings. All code snippets are runnable (except for omitted package main and main declarations).

Generic Syntax

Functions can have a type‑parameter list in square brackets, e.g., func F[T any](p T) { ... }.

Type parameters may be used in function parameters and bodies.

Types can also have type parameters, e.g., type M[T any] []T.

Each type parameter has a constraint; any matches any type.

Constraints are expressed with interface, e.g., type Integer1 interface { int }.

Union types use |, e.g.,

type Integer2 interface { int | int8 | int16 | int32 | int64 }

.

The ~T syntax matches underlying types, e.g., type Integer3 interface { ~int }.

This design has been proposed and accepted as a future language change. We currently expect that this change will be available in the Go 1.18 release in early 2022. Type Parameters Proposal

Higher‑Order Functions

Higher‑order functions (HOFs) either accept functions as arguments or return functions. Go supports closures, so HOFs are straightforward:

func foo(bar func() string) func() string {
    return func() string {
        return "foo" + " " + bar()
    }
}

func main() {
    bar := func() string { return "bar" }
    foobar := foo(bar)
    fmt.Println(foobar()) // Output: foo bar
}

A generic Filter HOF can be written as:

func Filter[T any](f func(T) bool, src []T) []T {
    var dst []T
    for _, v := range src {
        if f(v) {
            dst = append(dst, v)
        }
    }
    return dst
}

func main() {
    src := []int{-2, -1, 0, 1, 2}
    dst := Filter(func(v int) bool { return v >= 0 }, src)
    fmt.Println(dst) // Output: [0 1 2]
}

Code‑Generation Dilemma

Before generics, two approaches existed: using interface{} with reflection (sacrificing type safety and performance) or writing separate functions for each concrete type (high duplication). Code generation mitigates duplication but cannot handle arbitrary type combinations, especially for functions like Map that require multiple type parameters.

Generics allow a single signature such as func Map[T1, T2 any](f func(T1) T2, src []T1) []T2, avoiding the explosion of specialized functions.

Unsugared Generics

Go’s syntax is verbose compared to languages like Haskell. For example, anonymous functions in Haskell are concise, while Go requires full type signatures for closures, leading to boilerplate.

filter \x -> x >= 0 $ [-2,-1,0,1,2]
-- Output: [0,1,2]

Proposals such as Lightweight anonymous function syntax aim to simplify closure syntax, but they are not expected before Go 2.

Method Type Parameters

Methods cannot have their own type parameters; only the receiver type may have them. This restriction simplifies the language but limits expressiveness for generic methods like Map on a generic list.

This design does not permit methods to declare type parameters that are specific to the method. The receiver may have type parameters, but the method may not add any type parameters.

Lazy Evaluation

Lazy evaluation defers computation until the result is needed, reducing space complexity. A lazy version of Add returns a closure:

func Add(a, b int) int { return a + b }

func LazyAdd(a, b int) func() int { return func() int { return a + b } }

Chaining lazy Filter calls can keep only one slice in memory, achieving O(N) space instead of O(k·N).

type Iter[T any] interface { Next() (T, bool) }

type SliceIter[T any] struct { i int; s []T }

func IterOfSlice[T any](s []T) Iter[T] { return &SliceIter[T]{s: s} }

func (i *SliceIter[T]) Next() (v T, ok bool) {
    if ok = i.i < len(i.s); ok {
        v = i.s[i.i]
        i.i++
    }
    return
}

A lazy filterIter implements Iter by applying a predicate on demand:

type filterIter[T any] struct { f func(T) bool; src Iter[T] }

func (i *filterIter[T]) Next() (v T, ok bool) {
    for {
        v, ok = i.src.Next()
        if !ok || i.f(v) {
            return
        }
    }
}

func Filter[T any](f func(T) bool, src Iter[T]) Iter[T] { return &filterIter[T]{f: f, src: src} }

Partial Application

Partial application fixes some arguments of a multi‑parameter function, returning a function that accepts the remaining arguments. Types such as FuncWith1Args and FuncWith2Args illustrate this concept.

type FuncWith1Args[A, R any] func(A) R

type FuncWith2Args[A1, A2, R any] func(A1, A2) R

func (f FuncWith2Args[A1, A2, R]) Partial(a1 A1) FuncWith1Args[A2, R] {
    return func(a2 A2) R { return f(a1, a2) }
}

Using generics, the boilerplate can be reduced by letting the compiler infer type arguments:

func Cast[A1, A2, R any](f FuncWith2Args[A1, A2, R]) FuncWith2Args[A1, A2, R] { return f }

f2 := Cast(Filter[int])

Variadic Type Parameters

Go does not support variadic type parameters, so separate generic types must be written for each arity (e.g., FuncWith3Args, FuncWith4Args), increasing code duplication.

Type System

Many FP features rely on a powerful type system, which Go currently lacks. For example, compile‑time type inspection is impossible; developers must resort to runtime type assertions or reflect. The ~T syntax introduces the notion of “underlying type,” but it cannot be used in type assertions, limiting expressiveness.

type Signed interface { ~int }

func IsSigned[T Signed](n T) {
    // cannot write: if _, ok := (interface{})(n).(Signed); ok { ... }
}

These gaps prevent full implementation of advanced FP concepts such as type classes or algebraic data types.

Conclusion

Generics enable many FP features to be expressed more generally.

They offer greater flexibility than code generation, though numerous edge‑case issues remain.

Go 1.18’s generics add only the type‑parameter syntax without broader language changes, leading to some mismatches with existing language design.

Limitations of Go’s type system prevent a complete FP feature set.

References

Golang Functional Programming Brief – https://hedzr.com/golang/fp/golang-functional-programming-in-brief/

GopherCon 2020 – Functional Programming with Go – https://www.youtube.com/watch?v=wqs8n5Uk5OM

Spec: add generic programming using type parameters #43651 – https://github.com/golang/go/issues/43651

Type Parameters Proposal – https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md

Compile Go from source – https://golang.org/doc/install/source#install

go2go Playground – https://go2goplay.golang.org/

#Very high level overview – https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#very-high-level-overview

Higher‑order functions – https://zh.wikipedia.org/wiki/高阶函数

Code‑generation examples – https://github.com/golang/go/search?q=filename%3Agen.go

hasgo – https://pkg.go.dev/github.com/DylanMeeus/hasgo/types?utm_source=godoc#Ints.Map

functional‑go – https://pkg.go.dev/github.com/logic-building/functional-go/fp

fpGo – https://pkg.go.dev/github.com/TeaEntityLab/fpGo#Map

Lightweight anonymous function syntax proposal – https://github.com/golang/go/issues/21498

Method chaining – https://wikipedia.org/wiki/Method_chaining

#No parameterized methods – https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#No-parameterized-methods

Partial application – https://wikipedia.org/wiki/Partial_application

Currying – https://zh.wikipedia.org/wiki/柯里化

Partial Function Application is not Currying – https://www.uncarved.com/articles/not-currying/

Closure syntax discussion – https://silverrainz.me/blog/funtional-programming-in-go-generics.html#id18

#Type inference – https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#type-inference

#Type inference for composite literals – https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#type-inference-for-composite-literals

#Omissions – https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#omissions

Code‑generation dilemma – https://silverrainz.me/blog/funtional-programming-in-go-generics.html#id12

#Identifying the matched predeclared type – https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#identifying-the-matched-predeclared-type

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

GoGenericsType Parametersfunctional programmingHigher-Order Functions
MaGe Linux Operations
Written by

MaGe Linux Operations

Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.