Programación funcional en Swift: de closures a composición
Swift fue diseñado originalmente como un lenguaje multiparadigma. Soporta programación orientada a objetos, orientada a protocolos y funcional. El enfoque funcional ayuda a escribir código más predecible, testeable y seguro para la concurrencia. En este artículo, analizaremos los conceptos clave de la programación funcional en Swift con ejemplos prácticos.
1. Funciones puras e inmutabilidad
La base del estilo funcional son las funciones puras. Una función así siempre devuelve el mismo resultado para los mismos argumentos y no tiene efectos secundarios (no cambia el estado externo).
// Función impura — modifica una variable externavar total = 0func addToTotal(_ value: Int) -> Int { total += value return total}
// Función pura — solo cálculofunc add(_ a: Int, _ b: Int) -> Int { return a + b}La inmutabilidad se logra mediante let y estructuras con semántica de valor. En lugar de modificar un array, creamos uno nuevo:
let numbers = [1, 2, 3, 4, 5]// En lugar de var y append:let newNumbers = numbers + [6] // [1, 2, 3, 4, 5, 6]2. Closures como bloques de construcción
Los closures en Swift son funciones anónimas que pueden capturar variables del contexto circundante. Son "ciudadanos de primera clase": se pueden pasar como parámetros y devolver desde funciones.
// Closure simplelet multiply: (Int, Int) -> Int = { $0 * $1 }let result = multiply(3, 4) // 12
// Closure con captura de contextovar counter = 0let increment = { counter += 1 }increment() // counter = 1increment() // counter = 2Los closures se utilizan activamente en métodos de orden superior, como sorted:
let names = ["Иван", "Анна", "Борис", "Елена"]let sorted = names.sorted { $0.localizedStandardCompare($1) == .orderedAscending }print(sorted) // ["Анна", "Борис", "Елена", "Иван"]3. Métodos de orden superior: map, filter, reduce
Estos son el "caballo de batalla" del estilo funcional en Swift. Permiten procesar colecciones sin bucles explícitos ni mutación.
map
Transforma cada elemento de la colección:
let prices = [100, 200, 300]let withTax = prices.map { $0 * 1.2 }print(withTax) // [120.0, 240.0, 360.0]
// Ejemplo real: extraer nombres de un array de modelosstruct User { let name: String let age: Int}let users = [User(name: "Алексей", age: 30), User(name: "Мария", age: 25)]let userNames = users.map { $0.name }print(userNames) // ["Алексей", "Мария"]filter
Selecciona elementos según una condición:
let numbers = [1, 2, 3, 4, 5, 6]let evenNumbers = numbers.filter { $0 % 2 == 0 }print(evenNumbers) // [2, 4, 6]
// Filtrar usuarios mayores de 18 añoslet adults = users.filter { $0.age >= 18 }reduce
Reduce la colección a un solo valor:
let sum = numbers.reduce(0, +) // 21// Construir una cadena a partir de un arraylet words = ["Функциональное", "программирование", "Swift"]let sentence = words.reduce("") { $0.isEmpty ? $1 : $0 + " " + $1 }print(sentence) // "Функциональное программирование Swift"Combinación de métodos
Lo más interesante son las cadenas de llamadas:
let transactions = [250, -50, 100, -200, 300, -100]let balance = transactions .filter { $0 > 0 } // solo ingresos .map { $0 * 0.87 } // restamos impuesto del 13% .reduce(0, +) // sumamosprint(balance) // 565.54. Composición de funciones y currying
La composición permite combinar varias funciones en una. Swift no tiene un operador de composición incorporado, pero podemos crear el nuestro: