Functional Programming in Go: Advanced Level
\n\nGo (Golang) is not a purely functional language, but its powerful type system, support for closures, and first-class functions allow functional paradigms to be applied effectively. At an advanced level, functional programming in Go helps write more predictable, testable, and concurrency-safe code. In this article, we will explore key concepts: pure functions, composition, closures, currying, monadic patterns, and error handling without exceptions.
\n\n1. Pure Functions and Immutability
\n\nA pure function is a function that:
\n- \n
- Always returns the same result for the same arguments (determinism). \n
- Has no side effects (does not modify external state, does not write to files/network). \n
In Go, immutability is achieved by passing copies of data and using constants. Example of a pure function:
\n\npackage main\n\nimport "fmt"\n\n// Pure function: does not modify the original slice, returns a new one\nfunc DoubleSlice(s []int) []int {\n result := make([]int, len(s))\n for i, v := range s {\n result[i] = v * 2\n }\n return result\n}\n\nfunc main() {\n original := []int{1, 2, 3}\n doubled := DoubleSlice(original)\n fmt.Println(original) // [1 2 3] — unchanged\n fmt.Println(doubled) // [2 4 6]\n}\n\nUsing pure functions simplifies refactoring and testing, as there is no hidden state to account for.
\n\n2. Higher-Order Functions and Closures
\n\nIn Go, functions are first-class objects: they can be passed as arguments, returned from other functions, and stored in variables. A closure is a function that captures variables from its outer scope.
\n\nExample of a higher-order function — Filter:
package main\n\nimport "fmt"\n\n// Filter returns a new slice containing elements that satisfy the predicate\nfunc Filter[T any](s []T, predicate func(T) bool) []T {\n var result []T\n for _, v := range s {\n if predicate(v) {\n result = append(result, v)\n }\n }\n return result\n}\n\nfunc main() {\n numbers := []int{1, 2, 3, 4, 5, 6}\n even := Filter(numbers, func(n int) bool { return n%2 == 0 })\n fmt.Println(even) // [2 4 6]\n}\n\nClosures are often used to create function factories or to encapsulate state:
\n\npackage main\n\nimport "fmt"\n\n// Counter creates a closure that stores internal state\nfunc Counter() func() int {\n count := 0\n return func() int {\n count++\n return count\n }\n}\n\nfunc main() {\n c1 := Counter()\n fmt.Println(c1()) // 1\n fmt.Println(c1()) // 2\n\n c2 := Counter()\n fmt.Println(c2()) // 1 — independent state\n}\n\n3. Function Composition and Partial Application (Currying)
\n\nComposition is combining multiple functions into one new function. In Go, composition is implemented through intermediate functions. Partial application (currying) allows fixing some arguments and returning a function that expects the remaining ones.
\n\nExample of composition:
\n\npackage main\n\nimport "fmt"\n\n// Compose combines two functions: f(g(x))\nfunc Compose[A, B, C any](f func(B) C, g func(A) B) func(A) C {\n return func(x A) C {\n return f(g(x))\n }\n}\n\nfunc addOne(x int) int { return x + 1 }\nfunc double(x int) int { return x * 2 }\n\nfunc main() {\n addThenDouble := Compose(double, addOne)\n fmt.Println(addThenDouble(3)) // (3+1)*2 = 8\n}\n\nCurrying example with a sum function:
\n\npackage main\n\nimport "fmt"\n\n// Add — curried version of addition\nfunc Add(a int) func(int) int {\n return func(b int) int {\n return a + b\n }\n}\n\nfunc main() {\n addFive := Add(5)\n fmt.