Funções como valores e tipos de função
1. Funções como Cidadãos de Primeira Classe
Em Go, funções são cidadãos de primeira classe, o que significa que podem ser atribuídas a variáveis, passadas como argumentos e retornadas de outras funções. Essa característica é fundamental para escrever código flexível e reutilizável.
package main
import "fmt"
// Atribuição de função a variável
var soma = func(a, b int) int {
return a + b
}
func main() {
resultado := soma(3, 4)
fmt.Println(resultado) // 7
// Função como argumento
executarOperacao(10, 5, soma)
}
func executarOperacao(x, y int, operacao func(int, int) int) {
fmt.Println("Resultado:", operacao(x, y))
}
2. Sintaxe de Tipos de Função
A declaração explícita de tipos de função melhora a legibilidade e permite a reutilização de assinaturas complexas.
package main
import "fmt"
// Declaração de tipo de função nomeado
type Operacao func(int, int) int
// Tipo anônimo vs nomeado
type Transformador func(string) string
func main() {
var op Operacao = func(a, b int) int {
return a * b
}
// Uso de tipo nomeado
resultado := aplicarOperacao(6, 7, op)
fmt.Println("Multiplicação:", resultado) // 42
// Tipo anônimo inline
aplicarOperacao(10, 2, func(a, b int) int {
return a / b
})
}
func aplicarOperacao(x, y int, op Operacao) int {
return op(x, y)
}
3. Funções como Argumentos (Callbacks)
Funções de alta ordem que recebem outras funções como argumentos são extremamente úteis para processamento de dados.
package main
import "fmt"
type Filtro func(int) bool
type Mapeador func(int) int
func filtrar(numeros []int, fn Filtro) []int {
var resultado []int
for _, n := range numeros {
if fn(n) {
resultado = append(resultado, n)
}
}
return resultado
}
func mapear(numeros []int, fn Mapeador) []int {
resultado := make([]int, len(numeros))
for i, n := range numeros {
resultado[i] = fn(n)
}
return resultado
}
func reduzir(numeros []int, fn func(int, int) int, inicial int) int {
acumulador := inicial
for _, n := range numeros {
acumulador = fn(acumulador, n)
}
return acumulador
}
func main() {
dados := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// Filtro com função anônima
pares := filtrar(dados, func(n int) bool {
return n%2 == 0
})
fmt.Println("Pares:", pares) // [2 4 6 8 10]
// Mapeamento
dobrados := mapear(pares, func(n int) int {
return n * 2
})
fmt.Println("Dobrados:", dobrados) // [4 8 12 16 20]
// Redução (soma)
soma := reduzir(dobrados, func(acc, n int) int {
return acc + n
}, 0)
fmt.Println("Soma:", soma) // 60
}
4. Funções como Valores de Retorno
Closures e fábricas de funções permitem criar funções personalizadas dinamicamente.
package main
import "fmt"
// Fábrica de funções
func criarMultiplicador(fator int) func(int) int {
return func(x int) int {
return x * fator
}
}
// Closure com estado
func contador() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
// Usando fábrica de funções
duplicar := criarMultiplicador(2)
triplicar := criarMultiplicador(3)
fmt.Println(duplicar(5)) // 10
fmt.Println(triplicar(5)) // 15
// Usando closure
c1 := contador()
fmt.Println(c1()) // 1
fmt.Println(c1()) // 2
fmt.Println(c1()) // 3
// Cuidado: captura em loops (Go 1.22+ corrige isso)
funcoes := make([]func(), 3)
for i := 0; i < 3; i++ {
i := i // Necessário em versões anteriores
funcoes[i] = func() {
fmt.Println(i)
}
}
funcoes[0]() // 0
funcoes[1]() // 1
funcoes[2]() // 2
}
5. Comparação e Igualdade de Funções
Funções em Go só podem ser comparadas com nil, não entre si.
package main
import (
"fmt"
"reflect"
)
func main() {
var fn1 func(int) int = func(x int) int { return x * 2 }
var fn2 func(int) int = func(x int) int { return x * 2 }
// Comparação com nil
if fn1 != nil {
fmt.Println("fn1 não é nil")
}
// Tentativa de comparação direta (erro de compilação)
// if fn1 == fn2 { ... } // Ilegal!
// Uso de reflect para verificar igualdade
same := reflect.ValueOf(fn1).Pointer() == reflect.ValueOf(fn2).Pointer()
fmt.Println("Mesma função:", same) // false (diferentes closures)
// Funções não podem ser chaves de map
// m := make(map[func()]string) // Erro de compilação!
}
6. Tipos de Função e Interfaces
O padrão de adaptador permite que funções satisfaçam interfaces.
package main
import (
"fmt"
"net/http"
)
// Interface simples
type Logger interface {
Log(mensagem string)
}
// Adaptador: função que implementa Logger
type LoggerFunc func(string)
func (f LoggerFunc) Log(mensagem string) {
f(mensagem)
}
// Exemplo com http.HandlerFunc
func meuHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Olá, %s!", r.URL.Path[1:])
}
func main() {
// Usando adaptador
var logger Logger = LoggerFunc(func(msg string) {
fmt.Println("[LOG]:", msg)
})
logger.Log("Iniciando servidor...")
// http.HandlerFunc é outro exemplo de adaptador
http.Handle("/", http.HandlerFunc(meuHandler))
// Conversão implícita entre tipos compatíveis
type MinhaFuncao func(int) int
var f MinhaFuncao = func(x int) int { return x + 1 }
var g func(int) int = f // Conversão implícita
fmt.Println(g(5)) // 6
}
7. Métodos vs. Funções como Valores
Métodos podem ser tratados como valores de função de duas formas distintas.
package main
import "fmt"
type Calculadora struct {
valor int
}
func (c Calculadora) Somar(x int) int {
return c.valor + x
}
func (c *Calculadora) Incrementar(x int) {
c.valor += x
}
func main() {
calc := Calculadora{valor: 10}
// Method value: mantém o receptor
somar := calc.Somar
fmt.Println(somar(5)) // 15
// Method expression: receptor como primeiro parâmetro
somarExp := Calculadora.Somar
fmt.Println(somarExp(calc, 5)) // 15
// Com ponteiro
incrementar := calc.Incrementar
incrementar(3)
fmt.Println(calc.valor) // 13
// Passando método como callback
numeros := []int{1, 2, 3}
for _, n := range numeros {
fmt.Println(somar(n)) // 14, 15, 16
}
}
8. Boas Práticas e Padrões Comuns
package main
import (
"fmt"
"sort"
)
// Boa prática: nomear tipos de função claramente
type Comparador func(a, b int) bool
type Processador func(string) string
// Evitar complexidade excessiva
func processarLista(itens []string, fn Processador) []string {
resultado := make([]string, len(itens))
for i, item := range itens {
resultado[i] = fn(item)
}
return resultado
}
func main() {
// Nomeação clara
var cmp Comparador = func(a, b int) bool {
return a < b
}
numeros := []int{3, 1, 4, 1, 5}
sort.Slice(numeros, func(i, j int) bool {
return cmp(numeros[i], numeros[j])
})
fmt.Println(numeros) // [1 1 3 4 5]
// Quando preferir interfaces a tipos de função
// Use interface quando houver múltiplos métodos
// Use tipo de função para callbacks simples
nomes := []string{"ana", "joão", "maria"}
maiusculas := processarLista(nomes, func(s string) string {
return string(s[0]-32) + s[1:] // Simulação de maiúscula
})
fmt.Println(maiusculas) // [Ana João Maria]
}
Referências
- Documentação Oficial: Tipos de Função — Especificação completa dos tipos de função na linguagem Go
- Go by Example: Closures — Exemplos práticos de closures e funções como valores
- The Go Blog: Functions as Values — Artigo oficial sobre funções como cidadãos de primeira classe em Go
- Golangbot: First Class Functions — Tutorial detalhado sobre funções de alta ordem e tipos de função
- Effective Go: Functions — Guia oficial com boas práticas para uso de funções em Go
- Go 101: Function Types and Values — Explicação aprofundada sobre tipos de função e valores funcionais
- Dave Cheney: Functional Options — Padrão avançado usando funções como valores para APIs amigáveis