Programação Funcional em Swift: Nível Avançado
Swift foi originalmente projetado como uma linguagem multiparadigma, o que o torna uma plataforma ideal para aplicar técnicas avançadas de programação funcional. Neste artigo, exploraremos conceitos que vão além de closures básicas e map/filter/reduce — falaremos sobre functores, mônadas, composição de funções, currying e memoização. Essas ferramentas ajudarão você a escrever código mais previsível, testável e expressivo para aplicativos iOS e macOS.
Functores e Mônadas: Trabalhando com Contextos
Um functor é um tipo que implementa a função map, permitindo aplicar uma transformação a um valor dentro de um contexto. Em Swift, exemplos clássicos de functores são Optional e Array.
// Functor Optionallet value: Int? = 42let result = value.map { $0 * 2 } // Optional(84)
// Functor Arraylet numbers = [1, 2, 3]let doubled = numbers.map { $0 * 2 } // [2, 4, 6]Uma mônada é um functor com uma operação adicional flatMap (ou >>= em Haskell). Ela permite "desembrulhar" contextos aninhados. Swift suporta mônadas implicitamente através do protocolo Monad, mas tipos-chave (Optional, Result, Async) implementam essa lógica.
// Mônada Optional: encadeamento de operações com possível nilfunc parseNumber(_ string: String) -> Int? { return Int(string) }func half(_ number: Int) -> Int? { return number % 2 == 0 ? number / 2 : nil }
let input = "42"let result = input.flatMap(parseNumber).flatMap(half) // Optional(21)
// Mônada Result para tratamento de errosenum AppError: Error { case invalidInput }func safeDivide(_ a: Int, _ b: Int) -> Result<Int, AppError> { guard b != 0 else { return .failure(.invalidInput) } return .success(a / b)}
let divisionResult = safeDivide(10, 2).flatMap { safeDivide($0, 1) } // .success(5)Aplicação avançada: criação de um tipo monádico personalizado para trabalhar com operações assíncronas.
struct Async<T> { let run: (@escaping (T) -> Void) -> Void func map<U>(transform: @escaping (T) -> U) -> Async<U> { return Async<U> { callback in self.run { value in callback(transform(value)) } } } func flatMap<U>(transform: @escaping (T) -> Async<U>) -> Async<U> { return Async<U> { callback in self.run { value in transform(value).run(callback) } } }}Composição de Funções: Construindo Pipelines
Composição de funções é a criação de uma nova função aplicando sequencialmente várias funções. Em Swift, isso pode ser implementado através de um operador ou closures.
// Operador de composiçãoprecedencegroup CompositionPrecedence { associativity: left higherThan: FunctionArrowPrecedence}
infix operator >>> : CompositionPrecedence
func >>>(_ f: @escaping (A) -> B, _ g: @escaping (B) -> C) -> (A) -> C { return { x in g(f(x)) }}
// Exemplo: validação e formataçãolet trimWhitespace: (String) -> String = { $0.trimmingCharacters(in: .whitespaces) }let capitalize: (String) -> String = { $0.capitalized }let addExclamation: (String) -> String = { $0 + "!" }
let processString = trimWhitespace >>> capitalize >>> addExclamationprint(processString(" hello world ")) // "Hello World!"A composição é especialmente útil ao trabalhar com cadeias de transformações de dados, por exemplo, ao processar respostas de rede.
// Composição avançada com tratamento de errostypealias ResultFunction = (T) -> Result<U, Error>
func compose(_ f: @escaping ResultFunction<T, U>, _ g: @escaping ResultFunction<U, V>) -> ResultFunction<T, V> { return { input
```