Programação Funcional em Go: Nível Avançado
\n\nGo (Golang) não é uma linguagem puramente funcional, mas seu poderoso sistema de tipos, suporte a closures e funções de primeira classe permitem aplicar paradigmas funcionais de forma eficaz. Em nível avançado, a programação funcional em Go ajuda a escrever código mais previsível, testável e seguro para concorrência. Neste artigo, abordaremos conceitos-chave: funções puras, composição, closures, currying, padrões monádicos e tratamento de erros sem exceções.
\n\n1. Funções Puras e Imutabilidade
\n\nUma função pura é uma função que:
\n- \n
- Sempre retorna o mesmo resultado para os mesmos argumentos (determinismo). \n
- Não tem efeitos colaterais (não altera estado externo, não escreve em arquivos/rede). \n
Em Go, a imutabilidade é alcançada através da passagem de cópias de dados e do uso de constantes. Exemplo de função pura:
\n\npackage main\n\nimport "fmt"\n\n// Função pura: não modifica a fatia original, retorna uma nova\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] — sem alterações\n fmt.Println(doubled) // [2 4 6]\n}\n\nO uso de funções puras simplifica a refatoração e os testes, pois não é necessário considerar estado oculto.
\n\n2. Funções de Ordem Superior e Closures
\n\nEm Go, funções são objetos de primeira classe: podem ser passadas como argumentos, retornadas de outras funções e armazenadas em variáveis. Um closure é uma função que captura variáveis do escopo externo.
\n\nExemplo de função de ordem superior — Filter:
package main\n\nimport "fmt"\n\n// Filter retorna uma nova fatia contendo elementos que satisfazem o predicado\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 são frequentemente usados para criar fábricas de funções ou para encapsular estado:
\n\npackage main\n\nimport "fmt"\n\n// Counter cria um closure que armazena estado interno\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 — estado independente\n}\n\n3. Composição de Funções e Aplicação Parcial (Currying)
\n\nComposição é a combinação de várias funções em uma nova. Em Go, a composição é implementada através de funções intermediárias. A aplicação parcial (currying) permite fixar parte dos argumentos e retornar uma função que espera os restantes.
\n\nExemplo de composição:
\n\npackage main\n\nimport "fmt"\n\n// Compose combina duas funções: 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 no exemplo de uma função de soma:
\n\npackage main\n\nimport "fmt"\n\n// Add — versão curried da adição\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.