Funciones lambda en Scala
⏱ Dedicación recomendada: 0 minutos
Esto considera el contenido visible y relevante, e ignora texto colapsado o marcado como opcional.
r8vnhill/scala-dibs
Una función lambda en Scala es una función anónima que puede ser tratada como un valor: asignada a una variable, pasada como argumento o devuelta desde otra función. La sintaxis general de una función lambda en Scala es:
(argumentos) => cuerpo
- Scala 3
- Scala 2
- Código esencial
- Código completo
val add = (x: Int, y: Int) => x + y
val add2 = add(2, _)
package cl.ravenhill
val add = (x: Int, y: Int) => x + y
val add2 = add(2, _)
@main def testAdd2(): Unit =
println(add2(3))
/* Output:
5
*/
- Código esencial
- Código completo
val add = (x: Int, y: Int) => x + y
val add2 = add(2, _)
package cl.ravenhill
object Lambdas {
private val add: (Int, Int) => Int = (x: Int, y: Int) => x + y
private val add2: Int => Int = add(2, _)
def main(args: Array[String]): Unit = {
println(add2(3))
/* Output:
5
*/
}
}
Lambdas en Scala con funciones de orden superior
Al igual que en Kotlin, las lambdas en Scala se utilizan comúnmente con funciones de orden superior.
Por ejemplo, usando filter
en una lista:
- Scala 3
- Scala 2
- Código esencial
- Código completo
val numbers = List(1, 2, 3, 4, 5)
val evenNumbers = numbers.filter(_ % 2 == 0)
package cl.ravenhill
@main def mainFilter(): Unit =
val numbers = List(1, 2, 3, 4, 5)
val evenNumbers = numbers.filter(_ % 2 == 0)
println(evenNumbers)
/* Output:
List(2, 4)
*/
- Código esencial
- Código completo
val numbers = List(1, 2, 3, 4, 5)
val evenNumbers = numbers.filter(_ % 2 == 0)
package cl.ravenhill
object Filter {
def main(args: Array[String]): Unit = {
val numbers = List(1, 2, 3, 4, 5)
val evenNumbers = numbers.filter(_ % 2 == 0)
println(evenNumbers)
/* Output:
List(2, 4)
*/
}
}
Aplicando lambdas en funciones de orden superior
Puedes crear tus propias funciones de orden superior que acepten lambdas como parámetros:
- Scala 3
- Scala 2
- Código esencial
- Código completo
private def processInts(a: Int, b: Int, operation: (Int, Int) => Int): Int =
operation(a, b)
val sum = processInts(3, 4, _ + _)
package cl.ravenhill
private def processInts(a: Int, b: Int, operation: (Int, Int) => Int): Int =
operation(a, b)
@main def testProcessInts(): Unit =
val sum = processInts(3, 4, _ + _)
println(sum)
/* Output:
7
*/
- Código esencial
- Código completo
private def processInts(a: Int, b: Int, operation: (Int, Int) => Int): Int =
operation(a, b)
val sum = processInts(3, 4, _ + _)
package cl.ravenhill
object ProcessInts {
private def processInts(a: Int, b: Int, operation: (Int, Int) => Int): Int =
operation(a, b)
def main(args: Array[String]): Unit = {
val sum = processInts(3, 4, _ + _)
println(sum)
/* Output:
7
*/
}
}
- La función
processInts
acepta dos enteros y una lambda que define cómo operarlos. - Al llamar a
processInts
, proporcionamos una lambda que suma los dos números.
Desestructuración en lambdas
Scala permite desestructurar los parámetros de una lambda utilizando pattern matching directamente en la definición de la función anónima.
Ejemplo de desestructuración
- Scala 3
- Scala 2
- Código esencial
- Código completo
val pairs = List((1, 2), (3, 4), (5, 6))
val sums = pairs.map { (x, y) => x + y }
package cl.ravenhill
@main def testSumPairs(): Unit =
val pairs = List((1, 2), (3, 4), (5, 6))
val sums = pairs.map((x, y) => x + y)
println(sums)
/* Output:
List(3, 7, 11)
*/
- En este ejemplo,
(x, y)
desestructura cada tupla en sus componentesx
ey
. - La función lambda suma los componentes desestructurados.
- Código esencial
- Código completo
val pairs = List((1, 2), (3, 4), (5, 6))
val sums = pairs.map { case (x: Int, y: Int) => x + y }
package cl.ravenhill
object SumPairs {
def main(args: Array[String]): Unit = {
val pairs = List((1, 2), (3, 4), (5, 6))
val sums = pairs.map { case (x: Int, y: Int) => x + y }
println(sums)
/* Output:
List(3, 7, 11)
*/
}
}
- Usamos
case (x: Int, y: Int)
para desestructurar cada tupla en sus componentesx
ey
. - La función lambda suma los componentes desestructurados.
Scala permite desestructurar estructuras anidadas:
- Scala 3
- Scala 2
- Código esencial
- Código completo
val nestedPairs = List((1, (2, 3)), (4, (5, 6)))
val sums = nestedPairs.map { case (x, (y, z)) => x + y + z }
package cl.ravenhill
@main def testNestedSums(): Unit =
val nestedPairs = List((1, (2, 3)), (4, (5, 6)))
val sums = nestedPairs.map { case (x, (y, z)) => x + y + z }
println(sums)
/* Output:
List(6, 15)
*/
- Código esencial
- Código completo
val nestedPairs = List((1, (2, 3)), (4, (5, 6)))
val sums = nestedPairs.map { case (x, (y, z)) => x + y + z }
package cl.ravenhill
object NestedSums {
def main(args: Array[String]): Unit = {
val nestedPairs = List((1, (2, 3)), (4, (5, 6)))
val sums = nestedPairs.map { case (x, (y, z)) => x + y + z }
println(sums)
/* Output:
List(6, 15)
*/
}
}
Usamos case (x, (y, z))
para desestructurar cada tupla anidada en sus componentes x
, y
y z
.
Este nivel de desestructuración no es directamente posible en Kotlin dentro de lambdas.
Características adicionales de Scala en lambdas
Currificación y aplicación parcial
Scala soporta la currificación, permitiendo definir funciones que se pueden aplicar parcialmente:
def sumar(a: Int)(b: Int): Int = a + b
val sumarCinco = sumar(5) _
println(sumarCinco(10)) // Output: 15
Esto facilita la creación de funciones a partir de otras, fijando algunos parámetros.
Funciones de múltiples parámetros y listas de parámetros
Scala permite definir funciones con múltiples listas de parámetros:
def funcion(a: Int)(b: Int)(c: Int): Int = a + b + c
Esto es útil para currificación y para manejar parámetros implicitos (contextuales).
Tipos de función avanzados
Scala tiene un sistema de tipos más potente que Kotlin en cuanto a funciones:
- Funciones polimórficas: Puedes definir funciones que operan sobre tipos genéricos con más flexibilidad.
- Tipos de función de orden superior: Permite tipos de funciones que aceptan o devuelven otras funciones con tipos genéricos.
Diferencias entre Scala 2 y Scala 3
Scala 3 introduce varias mejoras en las lambdas:
- Inferencia de tipos mejorada: Permite omitir tipos en más lugares.
- Sintaxis más concisa: Se simplifica la escritura de lambdas y tipos.
- Funciones anónimas con múltiples parámetros: Se mejora la sintaxis para lambdas con varios parámetros.
Ejemplo de mejora en Scala 3
Scala 2:
val combinar: (Int, Int) => Int = (a: Int, b: Int) => a + b
Scala 3:
val combinar: (Int, Int) => Int = _ + _
En Scala 3, podemos usar _ + _
para indicar una función que suma dos parámetros.
Comparación con Kotlin
Característica | Scala | Kotlin |
---|---|---|
Desestructuración en lambdas | Soporta desestructuración completa, incluyendo anidada | Soporta desestructuración limitada, no en estructuras anidadas |
Placeholder en lambdas | Permite usar _ para parámetros anónimos en lambdas | Permite usar it para referirse al único parámetro en lambdas |
Currificación y múltiples listas de parámetros | Soporta currificación y funciones con múltiples parámetros | No soportado directamente |
Inferencia de tipos en lambdas | Inferencia más avanzada, especialmente en Scala 3 | Buena inferencia, pero menos flexible que Scala |
Pattern matching en lambdas | Soporta pattern matching completo en parámetros de lambdas | Limitado a desestructuración simple |
Sistema de tipos de funciones | Más expresivo y potente, con tipos de orden superior | Menos avanzado en este aspecto |
Funciones de contexto | Soportadas en Scala 3 (using clauses) | Soportadas mediante receptores de contexto (context receivers ) en versiones recientes |
Ventajas de Scala respecto a Kotlin en lambdas
Beneficios
- Mayor flexibilidad en desestructuración: Scala permite desestructurar parámetros en lambdas de forma más potente, incluyendo patrones anidados y pattern matching completo.
- Sintaxis más concisa con placeholders: El uso de
_
permite escribir lambdas de manera más compacta, pero el abuso de esta característica puede afectar la legibilidad. - Currificación nativa: Scala soporta currificación dentro de la firma de las funciones, lo que facilita la composición de funciones y la aplicación parcial.
- Sistema de tipos más avanzado: Permite expresar tipos de funciones más complejos y polimórficos.
- Pattern matching en lambdas: Scala permite usar pattern matching directamente en los parámetros de las lambdas, ofreciendo mayor expresividad.
Conclusión
Scala ofrece un conjunto de características avanzadas para trabajar con funciones lambda, proporcionando mayor flexibilidad y expresividad en comparación con Kotlin. Las mejoras introducidas en Scala 3 refuerzan estas capacidades, simplificando la sintaxis y ampliando la inferencia de tipos.
Mientras que Kotlin es conocido por su simplicidad y facilidad de uso, Scala se destaca por su poderoso sistema de tipos y sus características funcionales avanzadas, lo que lo convierte en una opción preferida para aquellos que buscan un mayor nivel de abstracción y funcionalidad en su código.