Programação Funcional em Swift: de A a Z
Programação Funcional (PF) é um paradigma que trata a computação como a avaliação de funções matemáticas e evita a mudança de estado e dados mutáveis. Swift, sendo uma linguagem multiparadigma, oferece excelente suporte ao estilo funcional, especialmente no desenvolvimento para iOS e macOS. Neste artigo, percorreremos desde conceitos básicos até técnicas avançadas, como mônadas e composição de funções.
Fundamentos da Abordagem Funcional em Swift
Na base da programação funcional estão vários princípios-chave:
- Funções puras — o resultado depende apenas dos argumentos de entrada, sem efeitos colaterais.
- Imutabilidade de dados — uso de
letem vez devar. - Funções como objetos de primeira classe — podem ser passadas, retornadas e armazenadas.
Vamos ver um exemplo simples de função pura:
// Função pura: sem efeitos colateraisfunc add(_ a: Int, _ b: Int) -> Int { return a + b}
// Usolet result = add(3, 5) // 8print(result)Closures e Funções de Ordem Superior
Closures são blocos autossuficientes de funcionalidade que podem ser passados e usados no código. Elas são a base para trabalhar com funções de ordem superior.
// Closure simpleslet multiply = { (x: Int, y: Int) -> Int in return x * y}
let product = multiply(4, 7) // 28print(product)Swift fornece poderosas funções de ordem superior para trabalhar com coleções:
map
map aplica uma transformação a cada elemento do array:
let numbers = [1, 2, 3, 4, 5]let squared = numbers.map { $0 * $0 }print(squared) // [1, 4, 9, 16, 25]filter
filter seleciona elementos que satisfazem uma condição:
let evenNumbers = numbers.filter { $0 % 2 == 0 }print(evenNumbers) // [2, 4]reduce
reduce combina todos os elementos em um único valor:
let sum = numbers.reduce(0, +)print(sum) // 15Composição de Funções e Currying
Composição permite combinar funções simples em funções mais complexas. Swift não possui um operador de composição nativo, mas é fácil implementá-lo:
// Operador de composiçãoprecedencegroup CompositionPrecedence { associativity: left higherThan: AssignmentPrecedence}
infix operator >>>: CompositionPrecedence
func >>> <A, B, C>(left: @escaping (A) -> B, right: @escaping (B) -> C) -> (A) -> C { return { right(left($0)) }}
// Usofunc double(_ x: Int) -> Int { x * 2 }func toString(_ x: Int) -> String { "\\(x)" }
let doubleThenString = double >>> toStringprint(doubleThenString(5)) // "10"Currying é a transformação de uma função que recebe múltiplos argumentos em uma sequência de funções, cada uma recebendo um único argumento:
// Função normalfunc add(_ a: Int, _ b: Int) -> Int { a + b }
// Versão curriedfunc curriedAdd(_ a: Int) -> (Int) -> Int { return { b in a + b }}
let addFive = curriedAdd(5)print(addFive(3)) // 8Mônadas e Funtores em Swift
Na programação funcional, um funtor é um tipo que pode ser mapeado (map). Uma mônada é um funtor com uma operação adicional flatMap (ou >>=). O exemplo mais conhecido em Swift são Optional e Array.
Optional como mônada
let optionalValue: Int? = 5
// map — aplica uma função se o valor não for nillet doubled = optionalValue.map { $0 * 2 }print(doubled) // Optional(10)
// flatMap