Programación funcional en Kotlin: nivel avanzado

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Programación funcional en Kotlin: nivel avanzado

Kotlin es un lenguaje multiparadigma que combina de manera orgánica los enfoques orientado a objetos y funcional. Si ya dominas los conceptos básicos de la programación funcional (PF), como map, filter, fold y lambdas, es hora de sumergirte en técnicas avanzadas. En este artículo analizaremos la composición de funciones, el currying, la evaluación perezosa y también conoceremos los functores y mónadas con ejemplos en Kotlin. Todos los ejemplos de código se pueden ejecutar en tu IDE.



1. Composición de funciones y currying

La composición es la creación de una nueva función mediante la aplicación secuencial de dos o más funciones. Kotlin no tiene un operador de composición incorporado como Haskell, pero se puede implementar fácilmente mediante funciones de extensión.



Ejemplo de composición de funciones

// Definimos las funcionesval double: (Int) -> Int = { it * 2 }val increment: (Int) -> Int = { it + 1 }

// Composición: primero double, luego incrementinfix fun <A, B, C> ((B) -> C).compose(f: (A) -> B): (A) -> C = { a -> this(f(a)) }

val doubleThenIncrement = increment compose doubleprintln(doubleThenIncrement(5)) // 11 (5*2 + 1)


Currying

El currying es la transformación de una función de varios argumentos en una cadena de funciones, cada una de un solo argumento. En Kotlin esto se implementa mediante lambdas anidadas.

// Función normalfun sum(a: Int, b: Int): Int = a + b

// Versión currificadafun curriedSum(a: Int): (Int) -> Int = { b -> a + b }

val add5 = curriedSum(5)println(add5(3)) // 8

// Función universal para curryingfun <A, B, C> ((A, B) -> C).curry(): (A) -> (B) -> C = { a -> { b -> this(a, b) } }

val curriedSum2 = ::sum.curry()println(curriedSum2(10)(20)) // 30

El currying es especialmente útil al trabajar con aplicación parcial y fábricas de funciones.



2. Functores y mónadas: teoría en la práctica

Un functor es un contenedor que se puede mapear (map) mediante una función. Una mónada es un functor con la capacidad de componer contenedores (flatMap). En Kotlin, un ejemplo claro de mónada es Result (o Either), así como Sequence.



Ejemplo con Result (mónada Either)

data class User(val name: String, val email: String)

fun parseUser(json: String): Result<User> = runCatching { // Simulación de parseo if (json.isBlank()) throw IllegalArgumentException("Empty JSON") User("Alice", "alice@example.com")}

fun sendEmail(user: User): Result<String> = runCatching { if (user.email.contains("@")) "Email sent to ${user.email}" else throw IllegalArgumentException("Invalid email")}

// Cadena monádicaval result = parseUser("{...}") .flatMap { user -> sendEmail(user) } .map { message -> "Success: $message" } .onFailure { error -> println("Error: ${error.message}") }

println(result.getOrDefault("Failed"))

Aquí flatMap permite evitar comprobaciones anidadas de éxito/error, haciendo que el código sea lineal y legible.



Creación de nuestro propio functor: tipo Option

sealed class Option<out T> {    data class Some<T>(val value: T) : Option<T>()    object None : Option<Nothing>()}

fun <T, R> Option<T>.map(f: (T) -> R): Option<R> = when (this) { is Option.Some -> Option.Some(f(value)) is Option.None -> Option.None}

fun <T, R> Option<T>.flatMap(f: (T) -> Option<R>): Option<R> = when (this) { is Option.Some -> f(value) is Option.None -> Option.None}

val maybeNumber: Option<Int> = Option.Some(42)val resultOption = maybeNumber .map { it * 2 } .flatMap { Option.Some("Value: $it") }

println(resultOption) // Some(value=Value: 84)

Blogs

Book Recommendations