Programación funcional en Swift: de la A a la Z
La programación funcional (PF) es un paradigma que considera los cálculos como la evaluación de funciones matemáticas y evita el cambio de estado y los datos mutables. Swift, al ser un lenguaje multiparadigma, admite excelentemente el estilo funcional, especialmente en el desarrollo para iOS y macOS. En este artículo, recorreremos el camino desde los conceptos básicos hasta las técnicas avanzadas, como las mónadas y la composición de funciones.
Fundamentos del enfoque funcional en Swift
En el corazón de la programación funcional se encuentran varios principios clave:
- Funciones puras — el resultado depende solo de los argumentos de entrada, sin efectos secundarios.
- Inmutabilidad de los datos — uso de
leten lugar devar. - Funciones como objetos de primera clase — se pueden pasar, devolver y almacenar.
Veamos un ejemplo simple de una función pura:
// Función pura: sin efectos secundariosfunc add(_ a: Int, _ b: Int) -> Int { return a + b}
// Usolet result = add(3, 5) // 8print(result)Closures y funciones de orden superior
Los closures son bloques de funcionalidad autocontenidos que se pueden pasar y utilizar en el código. Son la base para trabajar con funciones de orden superior.
// Closure simplelet multiply = { (x: Int, y: Int) -> Int in return x * y}
let product = multiply(4, 7) // 28print(product)Swift proporciona potentes funciones de orden superior para trabajar con colecciones:
map
map aplica una transformación a cada elemento del array:
let numbers = [1, 2, 3, 4, 5]let squared = numbers.map { $0 * $0 }print(squared) // [1, 4, 9, 16, 25]filter
filter selecciona los elementos que cumplen una condición:
let evenNumbers = numbers.filter { $0 % 2 == 0 }print(evenNumbers) // [2, 4]reduce
reduce combina todos los elementos en un solo valor:
let sum = numbers.reduce(0, +)print(sum) // 15Composición de funciones y currificación
La composición permite combinar funciones simples en otras más complejas. Swift no tiene un operador de composición incorporado, pero es fácil de implementar:
// Operador de composiciónprecedencegroup 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"La currificación es la transformación de una función que toma varios argumentos en una secuencia de funciones, cada una de las cuales toma un solo argumento:
// Función normalfunc add(_ a: Int, _ b: Int) -> Int { a + b }
// Versión currificadafunc curriedAdd(_ a: Int) -> (Int) -> Int { return { b in a + b }}
let addFive = curriedAdd(5)print(addFive(3)) // 8Mónadas y functores en Swift
En programación funcional, un funtor es un tipo que se puede mapear (map). Una mónada es un funtor con una operación adicional flatMap (o >>=). El ejemplo más conocido en Swift son Optional y Array.
Optional como mónada
let optionalValue: Int? = 5
// map — aplica una función si el valor no es nillet doubled = optionalValue.map { $0 * 2 }print(doubled) // Optional(10)
// flatMap