Go Functional Programming
Corresponds to video Ch6 (6-2 Functional Programming Example One), expanding on 002.md with more functional programming patterns
1. Review: Functions are First-Class Citizens in Go
Go is not a purely functional language, but functions can be used as:
- Variables
- Parameters
- Return values
- Stored in data structures
// 函数作为变量
var add = func(a, b int) int { return a + b }
// 函数作为参数
func apply(op func(int, int) int, a, b int) int {
return op(a, b)
}
// 函数作为返回值
func makeGreeter(greeting string) func(string) string {
return func(name string) string {
return greeting + ", " + name + "!"
}
}
func main() {
fmt.Println(apply(add, 3, 4)) // 7
hello := makeGreeter("Hello")
fmt.Println(hello("Go")) // Hello, Go!
}
2. Closure In-Depth
Closure = Function + its referenced external variables (free variables). The function and the variables it references together form a closure.
2.1 Accumulator (already in 002.md)
func adder() func(int) int {
sum := 0 // 自由变量,被闭包捕获
return func(v int) int {
sum += v
return sum
}
}
// a := adder()
// a(1) -> 1, a(2) -> 3, a(10) -> 13
2.2 Fibonacci Generator (already in 002.md)
func fibonacci() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}
2.3 Closure for Iterator Pattern
// 通用的整数序列迭代器
func intRange(start, end int) func() (int, bool) {
current := start
return func() (int, bool) {
if current > end {
return 0, false
}
val := current
current++
return val, true
}
}
func main() {
next := intRange(1, 5)
for v, ok := next(); ok; v, ok = next() {
fmt.Println(v) // 1 2 3 4 5
}
}
2.4 Closure Pitfall: Loop Variable Capture
// 错误示范
func main() {
var funcs []func()
for i := 0; i < 3; i++ {
funcs = append(funcs, func() {
fmt.Println(i) // 捕获的是变量 i 的地址,不是值
})
}
for _, f := range funcs {
f() // 输出 3 3 3,不是 0 1 2
}
}
// 正确做法:通过参数传值
func main() {
var funcs []func()
for i := 0; i < 3; i++ {
i := i // 重新声明局部变量(Go 的惯用写法)
funcs = append(funcs, func() {
fmt.Println(i)
})
}
for _, f := range funcs {
f() // 输出 0 1 2
}
}
3. Higher-Order Function Patterns
3.1 Map / Filter / Reduce
The Go standard library does not have generic versions of map/filter/reduce (Go 1.18+ allows implementing them with generics).
// Go 1.18+ 泛型实现
func Map[T, U any](s []T, f func(T) U) []U {
result := make([]U, len(s))
for i, v := range s {
result[i] = f(v)
}
return result
}
func Filter[T any](s []T, f func(T) bool) []T {
var result []T
for _, v := range s {
if f(v) {
result = append(result, v)
}
}
return result
}
func Reduce[T, U any](s []T, init U, f func(U, T) U) U {
result := init
for _, v := range s {
result = f(result, v)
}
return result
}
func main() {
nums := []int{1, 2, 3, 4, 5}
// 每个元素 *2
doubled := Map(nums, func(n int) int { return n * 2 })
fmt.Println(doubled) // [2 4 6 8 10]
// 过滤偶数
evens := Filter(nums, func(n int) bool { return n%2 == 0 })
fmt.Println(evens) // [2 4]
// 求和
sum := Reduce(nums, 0, func(acc, n int) int { return acc + n })
fmt.Println(sum) // 15
}
3.2 Function Composition (Compose)
// 将两个函数组合成一个
func Compose[T any](f, g func(T) T) func(T) T {
return func(x T) T {
return f(g(x))
}
}
func main() {
double := func(x int) int { return x * 2 }
addOne := func(x int) int { return x + 1 }
doubleAndAddOne := Compose(addOne, double)
fmt.Println(doubleAndAddOne(3)) // (3*2)+1 = 7
}
4. Decorator / Middleware Pattern
This is one of the most common practical scenarios for functional programming in Go, widely used in HTTP middleware.
4.1 Basic Decorator
// 给任意函数添加执行时间日志
func timeTrack(fn func()) func() {
return func() {
start := time.Now()
fn()
fmt.Printf("Execution time: %v\n", time.Since(start))
}
}
func main() {
slowFunc := func() {
time.Sleep(100 * time.Millisecond)
fmt.Println("done")
}
tracked := timeTrack(slowFunc)
tracked()
// done
// Execution time: 100.xxxms
}
4.2 HTTP Middleware Chain (Most common in real projects)
type Middleware func(http.HandlerFunc) http.HandlerFunc
// 日志中间件
func LoggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %s", r.Method, r.URL.Path)
next(w, r)
}
}
// 认证中间件
func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next(w, r)
}
}
// 链式组合中间件
func Chain(f http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc {
for i := len(middlewares) - 1; i >= 0; i-- {
f = middlewares[i](f)
}
return f
}
func main() {
hello := func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello!"))
}
// 请求流经: Logging → Auth → hello
http.HandleFunc("/api", Chain(hello, LoggingMiddleware, AuthMiddleware))
http.ListenAndServe(":8080", nil)
}
4.3 Comparing with errWrapper in 002.md
The errWrapper in 002.md is essentially a decorator pattern:
// 002.md 中的模式
type appHandler func(http.ResponseWriter, *http.Request) error
func errWrapper(handler appHandler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 统一错误处理逻辑
if err := handler(w, r); err != nil {
// 处理各种错误...
}
}
}
5. Functional Options Pattern
This is one of the most popular functional design patterns in the Go community, proposed by Dave Cheney and Rob Pike.
type Server struct {
host string
port int
timeout time.Duration
maxConn int
}
// Option 是一个函数类型
type Option func(*Server)
// 每个选项返回一个 Option 函数
func WithHost(host string) Option {
return func(s *Server) {
s.host = host
}
}
func WithPort(port int) Option {
return func(s *Server) {
s.port = port
}
}
func WithTimeout(timeout time.Duration) Option {
return func(s *Server) {
s.timeout = timeout
}
}
func WithMaxConn(maxConn int) Option {
return func(s *Server) {
s.maxConn = maxConn
}
}
// 构造函数接收可变数量的 Option
func NewServer(opts ...Option) *Server {
// 默认值
s := &Server{
host: "localhost",
port: 8080,
timeout: 30 * time.Second,
maxConn: 100,
}
// 应用所有选项
for _, opt := range opts {
opt(s)
}
return s
}
func main() {
// 灵活创建,只传需要的选项
s1 := NewServer()
s2 := NewServer(WithPort(9090), WithTimeout(60*time.Second))
s3 := NewServer(WithHost("0.0.0.0"), WithPort(443), WithMaxConn(1000))
fmt.Println(s1, s2, s3)
}
Advantages:
- No need to remember parameter order
- Adding new options does not break existing code
- Clear default values
- More Go-idiomatic than the builder pattern
6. Functional Programming for Tree Traversal (002.md Extension)
TraverseFunc is already in 002.md; here are more applications:
// 002.md 中的基础
func (node *Node) TraverseFunc(f func(*Node)) {
if node == nil { return }
node.Left.TraverseFunc(f)
f(node)
node.Right.TraverseFunc(f)
}
// 应用1:统计节点数
func (node *Node) Count() int {
count := 0
node.TraverseFunc(func(n *Node) {
count++
})
return count
}
// 应用2:求最大值
func (node *Node) Max() int {
maxVal := math.MinInt64
node.TraverseFunc(func(n *Node) {
if n.Value > maxVal {
maxVal = n.Value
}
})
return maxVal
}
// 应用3:收集所有值
func (node *Node) Collect() []int {
var result []int
node.TraverseFunc(func(n *Node) {
result = append(result, n.Value)
})
return result
}
7. Lazy Evaluation and Infinite Sequences
Simulating lazy evaluation using channels + goroutines:
// 无限自然数序列
func naturals() <-chan int {
ch := make(chan int)
go func() {
for i := 0; ; i++ {
ch <- i
}
}()
return ch
}
// take: 从 channel 中取前 n 个值
func take(n int, ch <-chan int) []int {
result := make([]int, n)
for i := 0; i < n; i++ {
result[i] = <-ch
}
return result
}
// 筛法求素数(经典的 CSP 并发+函数式示例)
func sieve() <-chan int {
out := make(chan int)
go func() {
ch := naturals()
<-ch // 跳过 0
<-ch // 跳过 1
for {
prime := <-ch
out <- prime
// 创建新的过滤器 goroutine
next := make(chan int)
go func(src <-chan int, p int) {
for v := range src {
if v%p != 0 {
next <- v
}
}
}(ch, prime)
ch = next
}
}()
return out
}
func main() {
// 取前 20 个素数
primes := take(20, sieve())
fmt.Println(primes)
// [2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71]
}
8. Functional Error Handling
An approach to avoid numerous if err != nil checks:
// 链式操作,遇到错误就停止
type Result struct {
val interface{}
err error
}
func (r Result) Then(f func(interface{}) (interface{}, error)) Result {
if r.err != nil {
return r // 已经有错误,直接透传
}
val, err := f(r.val)
return Result{val, err}
}
// 使用示例
func processFile(path string) Result {
return Result{path, nil}.
Then(func(v interface{}) (interface{}, error) {
return os.Open(v.(string))
}).
Then(func(v interface{}) (interface{}, error) {
return io.ReadAll(v.(*os.File))
}).
Then(func(v interface{}) (interface{}, error) {
data := v.([]byte)
return strings.ToUpper(string(data)), nil
})
}
Note: This pattern is not recommended for extensive use in production Go code, as it sacrifices type safety and readability. The Go community prefers the explicit
if err != nilstyle. This is merely a demonstration of functional programming concepts.
9. Summary
| Pattern | Application Scenarios in Go | Commonness |
|---|---|---|
| Closure | Lazy evaluation, state encapsulation, generators | Very Common |
| Higher-Order Functions | sort.Slice, http.HandleFunc | Very Common |
| Decorator/Middleware | HTTP middleware, logging, authentication, rate limiting | Very Common |
| Functional Options | Library/framework configuration API | Very Common |
| Map/Filter/Reduce | Data processing (more practical with Go 1.18+ generics) | Common |
| Function Composition | Pipeline processing | Occasional |
| Lazy Evaluation | Simulated with channel + goroutine | Occasional |
| Functional Error Handling | Experimental, not recommended for production use | Rare |
主题测试文章,只做测试使用。发布者:Walker,转转请注明出处:https://walker-learn.xyz/archives/6748