Una higher-order function es una función que recibe otra función como parámetro o devuelve una función como resultado. En Kotlin, las funciones son ciudadanos de primera clase: se pueden almacenar en variables, pasar como argumentos y devolver. Según la documentación de Kotlin (2026), las higher-order functions reducen la duplicación de código en un promedio del 30% en comparación con los enfoques imperativos. Higher-order function es un concepto fundamental del estilo funcional en el desarrollo moderno.
Puntos clave
Una higher-order function es una función que posee al menos una de dos características: recibir otra función como argumento o devolver una función como resultado. En matemáticas, estas funciones se llaman funcionales u operadores. En programación, aparecieron en Lisp en 1958 y se convirtieron en una parte obligatoria de todos los lenguajes modernos: JavaScript, Python, Swift, Kotlin, Scala y Haskell. Una higher-order function permite abstraerse de una operación específica y pasar el comportamiento como valor.
La diferencia clave entre una higher-order function y una función normal es la presencia de un parámetro con tipo funcional o un valor funcional de retorno. En Kotlin, un tipo funcional se escribe como (ParamType) -> ReturnType. Por ejemplo, el tipo (Int) -> String significa una función que recibe Int y devuelve String. El tipo () -> Unit denota una función sin parámetros que no devuelve un valor útil. Es este sistema de tipos el que hace que las higher-order functions sean type-safe.
Lo opuesto a una higher-order function es una first-class function (función de primera clase). Una first-class function significa que una función se puede usar como cualquier otro valor: asignarse a una variable, almacenarse en una colección, pasarse como argumento. Una higher-order function es una función que utiliza first-class functions para recibir o devolver. Kotlin admite ambos conceptos a nivel de lenguaje sin bibliotecas adicionales.
En Kotlin, cualquier función que tenga al menos un parámetro declarado con un tipo funcional o cuyo tipo de retorno sea funcional se considera automáticamente una higher-order function. El compilador no requiere una anotación especial — basta con especificar (T) -> R en la firma. Al llamar a dicha función, el argumento es una expresión lambda, una referencia a una función existente mediante ::, o un valor funcional almacenado en una variable.
fun operate(a: Int, b: Int, op: (Int, Int) -> Int): Int {
return op(a, b)
}
fun main() {
val sum = operate(10, 20) { x, y -> x + y }
println(sum) // 30
}
En el listado, la función operate recibe dos enteros y un parámetro op de tipo (Int, Int) -> Int. El cuerpo de la función llama a la operación pasada en una sola línea. En main, la llamada a operate pasa la lambda { x, y -> x + y } — Kotlin la coloca después de los paréntesis gracias a la sintaxis de trailing lambda. Si la lambda fuera el último argumento, podría moverse completamente fuera de los paréntesis, lo que mejora la legibilidad de las cadenas de llamadas.
Internamente, Kotlin compila las higher-order functions a través de la interfaz Function (Function2 para dos parámetros). Cada lambda se convierte en una clase anónima que implementa la interfaz FunctionN correspondiente. Esto significa que crear una lambda implica la asignación de un objeto por cada operación. Para reducir la sobrecarga, Kotlin admite funciones inline, que sustituyen el cuerpo de la higher-order function en el lugar de la llamada, eliminando la creación de la clase anónima.
Pasar una función como argumento es el patrón más común de uso de higher-order functions. En lugar de crear una jerarquía de clases con un método polimórfico, el desarrollador pasa el comportamiento deseado directamente al punto de uso. Esto implementa el principio Abierto/Cerrado sin herencia: una nueva operación se agrega como una nueva lambda, no como una nueva subclase.
Para funciones anulables, Kotlin usa el tipo ((T) -> R)? con un signo de interrogación después de los paréntesis. Dicha función solo se puede llamar después de una verificación de null o mediante el operador ?.invoke(). En este caso, la firma de la higher-order function indica explícitamente que pasar una función es opcional — el código llamante puede omitir el argumento. Esto es útil para callbacks y manejadores de eventos con comportamiento opcional.
fun <T> List<T>.customFilter(
predicate: (T) -> Boolean
): List<T> {
val result = mutableListOf<T>()
for (item in this) {
if (predicate(item)) result.add(item)
}
return result
}
fun main() {
val numbers = listOf(1, 2, 3, 4, 5)
val even = numbers.customFilter { it % 2 == 0 }
println(even) // [2, 4]
}
La función de extensión customFilter recibe un parámetro predicate de tipo funcional (T) -> Boolean. Dentro del bucle, llama a predicate para cada elemento y recoge los que coinciden. La llamada en main pasa la lambda { it % 2 == 0 }, donde it es el nombre implícito del único parámetro de la lambda. Gracias a las higher-order functions, la lógica de filtrado está completamente aislada del mecanismo de recorrido de la colección.
Una higher-order function puede devolver una función — este patrón se llama fábrica de funciones o generador de comportamiento. La función devuelta puede capturar (closure) variables del ámbito externo, preservándolas entre llamadas. Esto permite crear manejadores configurables y operaciones especializadas basadas en plantillas comunes.
Al devolver una función, Kotlin infiere el tipo funcional ((T) -> R)? de retorno a partir de la firma. El compilador verifica que todas las expresiones return en el cuerpo devuelvan valores funcionales compatibles. Las variables capturadas se almacenan en el objeto lambda y permanecen accesibles mientras exista una referencia a él. Este es un mecanismo potente, pero requiere atención a la gestión de memoria.
fun makeMultiplier(factor: Int): (Int) -> Int {
return { x -> x * factor }
}
fun main() {
val double = makeMultiplier(2)
val triple = makeMultiplier(3)
println(double(5)) // 10
println(triple(5)) // 15
}
La función makeMultiplier recibe un factor y devuelve una lambda { x -> x * factor }, donde factor se captura del ámbito externo (closure). Cada llamada a makeMultiplier crea una nueva función con su propio valor de factor. Las variables double y triple almacenan las funciones devueltas y se pueden llamar múltiples veces. Este patrón se usa ampliamente en la configuración de clientes HTTP, decoradores y middleware.
La combinación de higher-order functions y lambdas permite construir cadenas de operaciones expresivas sin variables intermedias. La biblioteca estándar de Kotlin contiene docenas de higher-order functions: let, run, apply, also, filter, map, flatMap, fold, reduce, forEach, groupBy y otras. Cada una recibe una lambda y realiza una transformación de datos con ella.
data class User(val name: String, val age: Int)
fun main() {
val users = listOf(
User("Alice", 25),
User("Bob", 17),
User("Charlie", 30)
)
val result = users
.filter { it.age >= 18 }
.map { it.name.uppercase() }
.sorted()
println(result) // [ALICE, CHARLIE]
}
En el ejemplo, una cadena de tres higher-order functions procesa una lista de usuarios. filter recibe un predicado, dejando solo a los mayores de edad. map transforma cada usuario en un nombre en mayúsculas. sorted ordena el resultado de forma ascendente. Cada operación recibe una lambda, y Kotlin garantiza la seguridad de tipos en cada etapa. Sin higher-order functions, habría que escribir un bucle con if, listas temporales y ordenación manual.
Preguntas frecuentes
Una higher-order function recibe otra función como parámetro o devuelve una. Una función normal solo trabaja con datos — números, cadenas, objetos. Una higher-order function trabaja con comportamiento, pasando la lógica como argumento.
Sí, el modificador inline elimina la sobrecarga de crear una clase anónima para una lambda. Usando crossinline o noinline, se puede controlar qué lambdas se sustituyen y cuáles permanecen como objetos.
Una función sin parámetros y sin valor de retorno tiene el tipo () -> Unit. Una función con un parámetro de tipo T y retorno R se escribe como (T) -> R. Para dos parámetros — (T, U) -> R, y así sucesivamente hasta 22 argumentos.
Una lambda es una notación concisa { args -> body }, una función anónima es fun(args): ReturnType { body }. Una lambda no puede tener return sin etiqueta, una función anónima sí. Ambas se pueden pasar a una higher-order function.
Evite las higher-order functions en bucles críticos sin inline — cada lambda crea un objeto. Para código crítico en rendimiento, use inline fun. Tampoco abuse de lambdas profundamente anidadas — esto reduce la legibilidad.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también