Kotlin - Scope Functions (let, with, run, apply & also)
Tags: kotlin, scopefunction, spanish
Published: 2021-02-22 00:36:49

Empecemos viendo este código:

```
  name?.let{
    //...
  }
```
Estoy seguro que en algún momento lo has visto y sabes que es una forma de validar que `name` no sea nulo antes de emplear el valor dentro del bloque de código dentro de la función `let`. Pero ¿realmente sabes las implicancias del uso de dicha función? Si la respuesta es no, pues esta publicación es para ti.

## Definiendo las _scope functions_
Según la [documentación oficial](https://kotlinlang.org/docs/scope-functions.html) son funciones cuyo único propósito es "ejecutar un bloque de código dentro del contexto de un objeto". Creo que la explicación es algo sencilla, básicamente es una forma de organizar nuestro código y hacerlo mejor leíble.

## ¿Qué _scope functions_ trae Kotlin?

Básicamente tenemos 5 _scope functions_: `let`, `with`, `run`, `apply` y `also`; adicionalmente hay 2 funciones extras `takeIf` y `takeUnless` que nos permitirán adicionar algunas validaciones de estado en medio de una cadena de invocación (o en tras palabras cuando concatenas varias _scope functions_) y que suelen emplearse junto con las 5 anteriores.

Como la documentación lo señala hay dos elementos que distinguen a una _scope function_ de otra:

* la forma en que se referencia al objeto de contexto
* el valor de retorno

### Por la forma en que se referencia al objeto de contexto
La razón por la que tenemos un `it` o un `this` es para simplificar o acortar el nombre de la variable original y,  básicamente, te dirá si el objeto va a recibir la lambda (para el caso del `this`) o si el objeto va a pasarse como argumento de la lambda (para el caso de `it`).

Entonces, ¿en qué casos el objeto de contexto va a recibir la lambda? `run`, `with` y `apply` y ¿en qué casos el objeto de contexto se pasará como argumento? `let` y `also`.

### Por el valor de retorno
Para esta situación tendremos el retorno del mismo objeto de contexto para el caso de `apply` y `also` o el valor de resultado de la lambda en el caso de `let`, `run` y `with`.

## ¿Qué función elegir?

Teniendo estas dos distinciones en mente, [la documentación oficial detalla cada una de las funciones](https://kotlinlang.org/docs/scope-functions.html#functions) y los posibles escenarios de uso. De esa misma documentación saqué este cuadro que ayuda mucho a resumir lo dicho anteriormente:

| Función | Referencia de objeto | Valor de retorno | ¿Es una _extension function_? |
|---|---|---|---|
| `let` | `it` | Resultado de la lambda | Sí |
| `run` | `this` | Resultado de la lambda | Sí |
| `run` | - | Resultado de la lambda | No, cuando se llama sin el objeto de contexto |
| `with` | `this` | Resultado de la lambda | No, porque toma el objeto de contexto como argumento |
| `apply` | `this` | Objeto de contexto | Sí | 
| `also` | `it` | Objeto de contexto | Sí | 

También la documentación brinda una pequeña guía de referencia:

- Ejecución de una lambda en objetos non-null: `let`
- Introducir una expresión como una variable en un ámbito local: `let`
- Configuración de un objeto: `apply`
- Configuración de un objeto y computación del resultado: `run`
- Ejecutar sentencias donde una expresión es requerida: `run` a secas sin objeto de contexto ni como función extendida
- Efectos adicionales: `also`
- Agrupar llamadas a funciones en un objeto: `with`

## takeIf y takeUnless
Estas dos funciones que vienen en la biblioteca estandar de Kotlin, no son consideradas _scope functions_; sin embargo, se emplean junto con ellas y sirven para realizar algunas validaciones. 

La documentación oficial [tiene algunos buenos ejemplos](https://kotlinlang.org/docs/scope-functions.html#takeif-and-takeunless) de cómo emplearlas.

## Conclusión
Me resultan super interesantes las _scope functions_ y este artículo tiene por intención hacer un breve resumen de en qué consisten y cuando emplearlas. Es importante practicar su uso para acostumbrarse a sus diferencias y casos de uso, dado que por lo similares que son resultado fácil confundirlas y equivocar su uso.