Programación funcional en Kotlin: 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. Kotlin, al ser un lenguaje multiparadigma, proporciona herramientas potentes para aplicar el enfoque funcional sin obligarte a abandonar la POO que ya conoces. En este artículo, analizaremos los conceptos clave de la PF en Kotlin: desde funciones puras e inmutabilidad hasta técnicas avanzadas como la composición y las mónadas.
1. Fundamentos del enfoque funcional: funciones puras e inmutabilidad
La base de la PF se construye sobre dos principios: la pureza de las funciones y la inmutabilidad de los datos.
- Función pura es una función cuyo resultado depende únicamente de los argumentos pasados y que no tiene efectos secundarios (no modifica variables externas, no imprime en consola, no accede a la base de datos).
- Inmutabilidad significa que los datos no pueden modificarse después de su creación. En lugar de cambiar un objeto existente, se crea una nueva copia con los cambios necesarios.
Ejemplo de función pura e impura en Kotlin:
// Función impura: tiene efecto secundario (salida en consola)fun impureSum(a: Int, b: Int): Int { println("Suma: ${a + b}") // efecto secundario return a + b}
// Función pura: solo cálculofun pureSum(a: Int, b: Int): Int = a + b
// Inmutabilidad: uso de val en lugar de vardata class User(val name: String, val age: Int) // val hace los campos inmutablesfun main() { val user = User("Alice", 30) // user.age = 31 // ¡Error de compilación! val olderUser = user.copy(age = 31) // creamos una nueva copia}El uso de val y data class con copy() es la forma estándar de mantener la inmutabilidad en Kotlin.
2. Funciones de orden superior y expresiones lambda
Kotlin admite funciones como objetos de primera clase. Esto significa que las funciones se pueden pasar como argumentos, devolver desde otras funciones y almacenar en variables. Las funciones que toman o devuelven otras funciones se denominan funciones de orden superior.
Las expresiones lambda son una forma compacta de crear funciones anónimas. Sintaxis: { argumentos -> cuerpo }.
// Función de orden superior: toma una función (Int, Int) -> Intfun operateOnNumbers(a: Int, b: Int, operation: (Int, Int) -> Int): Int { return operation(a, b)}
fun main() { // Expresiones lambda val sum: (Int, Int) -> Int = { x, y -> x + y } val multiply: (Int, Int) -> Int = { x, y -> x * y }
println(operateOnNumbers(5, 3, sum)) // 8 println(operateOnNumbers(5, 3, multiply)) // 15
// Uso de lambda "sobre la marcha" println(operateOnNumbers(10, 2, { a, b -> a - b })) // 8
// Si la lambda es el último argumento, se puede sacar fuera de los paréntesis (trailing lambda) println(operateOnNumbers(10, 2) { a, b -> a / b }) // 5}La biblioteca estándar de Kotlin utiliza activamente funciones de orden superior: map, filter, reduce para colecciones.
val numbers = listOf(1, 2, 3, 4, 5)val evenSquares = numbers .filter { it % 2 == 0 } // {2, 4} .map { it * it } // {4, 16}println(evenSquares) // [4, 16]3. Composición de funciones y currificación
Composición es el proceso de combinar dos o más funciones para crear una nueva función. Kotlin no tiene un operador de composición incorporado como Haskell, pero es fácil de implementar manualmente o con funciones de extensión.
// Composición de dos funcionesinfix fun ((B) -> C).compose(f: (A) -> B): (A) -> C = { a -> this(f(a)) }
fun main() { val toInt: (String) -> Int = { it.toInt() } val square: (Int) -> Int = { it * it }
val squ