Funciones lambda con receptores

Las funciones lambdas son funciones que permiten recibir funciones como parámetros, o bien, retornar una función. Este tipo de función cuya utilización es común en lenguajes con paradigma funcional como son los lenguajes Kotlin, Scala o Haskell. En lengueje Kotlin existe una variante de función lambda la cual permite recibir una referencia al objeto al que se le aplica la función; este tipo de función, se conoce como funciones lambda con receptores.

Para comprender las funciones lambda con receptores, mostraré unos ejemplos de funciones lambda: el primero, es un ejemplo de construcción de una cadena de texto; el segundo, realiza una transformación del tipo de entrada.

Ejemplo 1, función lambda.

Se define una función buildString que recibe como parámetro una función lambda cuyo parámetro de entrada es un elemento de tipo StringBuilder y su salida es un tipo Unit. En el siguiente ejemplo, la función buildString realiza la siguiente funcionalidad: instancia un objeto de la clase StringBuilder identificado como sb, aplicamos la función lambda pasada por parámetros con el objeto sb y, por último, retornamos el String resultante. Para finalizar el ejemplo, se define la invocación de la función y la impresión por consola del resultado.

El snippet del código es el siguiente:

fun buildString(
  builderAction: (StringBuilder) -> Unit
):String{
  val sb = StringBuilder()
  builderAction(sb)
  return sb.toString()
}
val s = buildString {
  it.append("Hello, ")
  it.append("World! ")
}
println("1.- Function Lambda=${s}")

La salida por consola es la siguiente:

1.- Function Lambda=Hello, World!

Ejemplo 2, función lambda

Se define una entidad con nombre Account, se define una función calculate30 la cual recibe como parámetro un elemento de tipo Account y una función lambda que transforma un tipo de entrada Account en otro tipo Account. La función calculate30 realiza las siguientes operaciones: creación de una instancia Account a partir del objeto pasado por parámetro de entrada y, por último, aplicación de la función lambda pasada por parámetro con el objeto Account creado previamente. Para finalizar el ejemplo, se realiza la creación del objeto account1, la invocación a la función calculate30 y la impresión por consola del resultado.

El snippet del código es el siguiente:

data class Account(
  val id: Int,
  val amount: Int,
  val result: Int,
  val status: String
)
fun calculate30(
  init: Account,
  func: (Account) -> Account): Account{
    val account = init.copy(amount = 30)
    return func(account)
}
val account1 = Account(id = 1, amount = 0, result = 0, status = "INIT")
val CTE = 3
val fLambda = calculate30(account1){
  it.copy( result = (it.amount * 6) * CTE, status = "END" )
}
println("1.- Account30 (lambda function)=${fLambda}")

La salida por consola es la siguiente:

1.- Account30 (lambda function)=Account(id=1, amount=30, result=540, status=END)

En los ejemplos anteriores, se puede apreciar que la definición de la función lambda se utiliza la palabre reservada “it” para poder trabajar con el objeto con el que se opera.

Funciones lambda con receptores

Las funciones lambda con receptores en Kotlin son como las funciones lambda a diferencia que la función trabaja con las funciones del tipo de entrada. La declaración de la función, se realiza como si operase con el objeto del tipo de entrada, pudiendo usar el operador this.

Ejemplo 1, función lambda con receptor.

Se define una función buildStringReceiver la cual tiene como parámetro de entrada una función lambda con receptor. La función buildStringReceiver realiza lo siguiente: creación del objeto s de tipo StringBuilder, invocación de la función lambda con el objeto creado y retorno del resultado. Para finalizar, se define la invocación a la función buildStringReceiver con la declaración de la función y la impresión del resultado.

fun buildStringReceiver(
  builderAction: StringBuilder.() -> Unit
): String{
    val s = StringBuilder()
    s.builderAction()
    return s.toString()
}
val r = buildStringReceiver{
  this.append("Hello, ")
  this.append("World! ")
}
println("2.- Function Lambda receiver=${r}")

La salida por consola es la siguiente:

2.- Function Lambda receiver=Hello, World!

Ejemplo 2, función lambda con receptor.

Se define una función calculate30Receiver la cual tiene como parámetro de entrada una función lambda con receptor. La función buildStringReceiver realiza lo siguiente:
creación del objeto de tipo StringBuilder, invocación de la función lambda con el objeto creado y retorno del resultado. Para finalizar, se define la invocación a la función buildStringReceiver con la declaración de la función y la impresión del resultado.

data class Account(
  val id: Int,
  val amount: Int,
  val result: Int,
  val status: String
)
fun calculate30Receiver(
  init: Account,
  func: Account.() -> Account
): Account{
    val account = init.copy(amount = 30)
    return account.func()
}
val fLReceiver = calculate30Receiver(account1){
  this.copy(result = (this.amount * 6) * CTE, status = "END" )
}
println("2.- Account30Receiver(lambda with receiver)=${fLReceiver}")

La salida por consola es la siguiente:

2.- Account30Receiver(lambda with receiver)=Account(id=1, amount=30, result=540, status=END)

Un ejemplo de función lambda con receptor en kotlin pueden ser las funciones apply o use.

Para el lector interesado, el código completo del ejemplo se encuentra en el siguiente enlace