Programação Funcional em Swift: de Closures à Composição
Swift foi originalmente projetado como uma linguagem multiparadigma. Ela suporta programação orientada a objetos, orientada a protocolos e funcional. A abordagem funcional ajuda a escrever código mais previsível, testável e thread-safe. Neste artigo, vamos explorar os conceitos-chave da programação funcional em Swift com exemplos práticos.
1. Funções Puras e Imutabilidade
A base do estilo funcional são as funções puras. Tal função sempre retorna o mesmo resultado para os mesmos argumentos e não tem efeitos colaterais (não altera o estado externo).
// Função impura — modifica uma variável externa
var total = 0
func addToTotal(_ value: Int) -> Int {
total += value
return total
}
// Função pura — apenas computação
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
A imutabilidade é alcançada através de let e structs com semântica de valor. Em vez de modificar um array, criamos um novo:
let numbers = [1, 2, 3, 4, 5]
// Em vez de var e append:
let newNumbers = numbers + [6] // [1, 2, 3, 4, 5, 6]
2. Closures como Blocos de Construção
Closures em Swift são funções anônimas que podem capturar variáveis do contexto circundante. Elas são "cidadãos de primeira classe": podem ser passadas como parâmetros e retornadas de funções.
// Closure simples
let multiply: (Int, Int) -> Int = { $0 * $1 }
let result = multiply(3, 4) // 12
// Closure com captura de contexto
var counter = 0
let increment = { counter += 1 }
increment() // counter = 1
increment() // counter = 2
Closures são amplamente utilizadas em métodos de ordem superior, como sorted:
let names = ["Ivan", "Anna", "Boris", "Elena"]
let sorted = names.sorted { $0.localizedStandardCompare($1) == .orderedAscending }
print(sorted) // ["Anna", "Boris", "Elena", "Ivan"]
3. Métodos de Ordem Superior: map, filter, reduce
Estes são o "cavalo de batalha" do estilo funcional em Swift. Eles permitem processar coleções sem loops explícitos e mutação.
map
Transforma cada elemento da coleção:
let prices = [100, 200, 300]
let withTax = prices.map { $0 * 1.2 }
print(withTax) // [120.0, 240.0, 360.0]
// Exemplo real: extrair nomes de um array de modelos
struct User {
let name: String
let age: Int
}
let users = [User(name: "Alexey", age: 30), User(name: "Maria", age: 25)]
let userNames = users.map { $0.name }
print(userNames) // ["Alexey", "Maria"]
filter
Seleciona elementos com base em uma condição:
let numbers = [1, 2, 3, 4, 5, 6]
let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers) // [2, 4, 6]
// Filtrar usuários com mais de 18 anos
let adults = users.filter { $0.age >= 18 }
reduce
Reduz a coleção a um único valor:
let sum = numbers.reduce(0, +) // 21
// Montar uma string a partir de um array
let words = ["Programação", "Funcional", "Swift"]
let sentence = words.reduce("") { $0.isEmpty ? $1 : $0 + " " + $1 }
print(sentence) // "Programação Funcional Swift"
Combinação de Métodos
O mais interessante são as cadeias de chamadas:
let transactions = [250, -50, 100, -200, 300, -100]
let balance = transactions
.filter { $0 > 0 } // apenas receitas
.map { $0 * 0.87 } // subtrai imposto de 13%
.reduce(0, +) // soma
print(balance) // 565.5
4. Composição de Funções e Currying
A composição permite combinar várias funções em uma. Swift não tem um operador de composição embutido