Las funciones de alto orden en Kotlin son funciones que pueden recibir otras funciones como parámetros, y se construyen con lambdas. Si vienes del mundo de la programación tradicional y te preguntas cómo pasar comportamiento en lugar de solo datos, aquí está la respuesta práctica para que la apliques en tu próximo proyecto.
¿Qué es una función de alto orden y por qué importa en Kotlin?
Una función de alto orden recibe o devuelve otras funciones. Esto te permite separar la lógica de negocio del comportamiento que quieres ejecutar después, algo muy común cuando trabajas con listas, eventos o respuestas asíncronas.
¿Qué es una lambda en Kotlin? Es una variable que representa una función. Tiene parámetros entre paréntesis, una flecha -> que indica el tipo de retorno, y un cuerpo entre llaves que produce el resultado.
El ejercicio arranca con una data class Email que tiene tres propiedades: subject, sender e isRead con valor por defecto en false [01:00]. A partir de ahí se construye una lista de correos para procesarlos con lambdas.
¿Cómo se declara una lambda y cómo accedo a sus parámetros?
La estructura básica es directa: declaras una val, defines los parámetros que recibe, indicas el tipo de retorno y abres llaves para escribir el cuerpo.
kotlin
val formatearEmail: (Email) -> String = {
"Asunto: ${it.subject} de ${it.sender}"
}
Dentro del cuerpo accedes al parámetro con la palabra reservada it, que en lambdas funciona como referencia implícita al argumento recibido [03:30]. La diferencia entre it y this es clara: it pertenece al contexto de una lambda, mientras que this apunta al objeto en clases.
Si prefieres un nombre más descriptivo, puedes renombrar el parámetro usando la sintaxis email -> .... Esto hace tu código más idiomático y legible cuando lo lea otra persona del equipo.
¿Por qué una lambda consume memoria distinta a una función normal?
Una lambda guardada en una variable vive en la memoria RAM del programa, mientras que una función definida dentro de una clase vive en el espacio de código. Es una diferencia sutil pero relevante cuando piensas en rendimiento.
¿Cómo aplico map y forEach con lambdas?
La función map transforma cada elemento de una colección aplicando la lambda que le pasas como parámetro. Si declaraste formatearEmail, basta con hacer emails.map(formatearEmail) para obtener una nueva lista de strings ya formateados [05:30].
Para recorrer e imprimir cada elemento, forEach es la opción natural. Como su único parámetro es una lambda, puedes saltar los paréntesis y escribir directamente las llaves:
kotlin
emailsFormateados.forEach { correo ->
println(correo)
}
También existe forEachIndexed, que entrega dos parámetros: el índice y el elemento. Es útil cuando necesitas mostrar la posición:
index: posición del elemento en la lista.
email: el objeto en sí mismo.
- Resultado: salidas tipo "correo número 0, reunión...".
Con esto ya tienes el patrón mental para usar lambdas como argumento de funciones existentes. Ahora viene la parte más interesante: crear tu propia función de alto orden.
¿Cómo creo una función que reciba un callback de éxito y error?
En la práctica, los callbacks son la forma más común de devolver resultados desde una función que puede tener distintos desenlaces, como una petición de red. La función enviarEmail recibe tres parámetros: el email, una lambda onSuccess y una lambda onError [09:00].
kotlin
fun enviarEmail(
email: Email,
onSuccess: (Email, String) -> Unit,
onError: (String, Int) -> Unit
) {
if (email.sender.contains("@")) {
onSuccess(email, "Mensaje enviado a ${email.sender}")
} else {
onError("Email inválido: ${email.sender}", 400)
}
}
La validación es deliberadamente simple: si el remitente contiene una arroba, se invoca onSuccess; si no, se invoca onError con un código 400, que en peticiones HTTP suele significar bad request.
¿Qué es un callback en programación? Es una función que se pasa como argumento a otra función para que se ejecute cuando ocurra un evento específico, como un éxito o un error. Permite reaccionar a resultados sin acoplar la lógica.
¿Cómo invoco la función con sus lambdas correspondientes?
Al llamar enviarEmail, pasas el objeto y luego defines en el sitio qué debe pasar en cada caso. Puedes renombrar los parámetros de cada lambda para que el código se lea con naturalidad:
kotlin
enviarEmail(
email1,
onSuccess = { e, mensaje -> println(mensaje) },
onError = { mensaje, error -> println("$mensaje, código $error") }
)
Cuando el correo tiene @, la salida es Reunión enviado a jefe@empresa.com. Cuando le quitas la arroba al sender, el resultado cambia a Email inválido: jefe.empresa, código 400 [14:00]. Ese contraste deja ver el poder real del patrón: una sola función orquesta dos caminos distintos sin condicionales en el código que la consume.
¿Has usado callbacks en tus proyectos? Cuéntame en los comentarios qué patrón sueles aplicar cuando necesitas manejar éxito y error en una misma función.