Las scope functions with y also en Kotlin resuelven dos necesidades muy concretas: ejecutar varias operaciones sobre un mismo objeto y disparar acciones complementarias sin alterar el flujo principal. Si ya conoces let, run y apply, estas dos cierran el set de herramientas que necesitas para escribir código más limpio y expresivo.
¿Cuándo usar with en Kotlin para operar sobre un objeto?
with se diferencia del resto porque no se invoca como una extensión del objeto, sino como una función independiente que recibe el objeto como argumento y una lambda con el bloque de instrucciones [0:24].
Imagina que tienes un email4 con propiedades como subject, body e isRead. Con with puedes agrupar todas las operaciones de configuración o lectura en un solo bloque:
kotlin
val summary = with(email4) {
isRead = true
"$id - $subject - leído: $isRead"
}
println("El resumen de email4 es: $summary")
La última expresión dentro del bloque es lo que se asigna a summary, igual que en otras scope functions. Aquí obtienes algo como IE - asunto importante - leído: true.
¿Cuándo conviene usar with en lugar de run? Usa with cuando vayas a ejecutar muchas operaciones o configuraciones extensas sobre el mismo objeto y necesites devolver un resultado calculado a partir de él.
¿Por qué with no se llama sobre el objeto?
Porque está pensado como un bloque aparte. Esa separación visual ayuda cuando el bloque crece y quieres dejar claro que estás trabajando sobre un objeto específico, no encadenando una transformación más en una cadena de llamadas.
¿Para qué sirve also en Kotlin y qué son los side effects?
also está hecho para esos momentos en los que necesitas ejecutar una acción complementaria que no transforma el objeto ni cambia su tipo. A esto se le llama side effect: algo que ocurre al lado del flujo principal.
El caso típico es disparar una acción justo después de crear el objeto. Por ejemplo, enviar el correo apenas se inicializa:
kotlin
val email5 = Email(...).also {
println("Enviando correo: $it")
}
Dentro del bloque accedes al objeto con it. Y aquí está la diferencia clave con run: mientras run puede transformar el valor de retorno hacia otro tipo, also te garantiza que el tipo del objeto asignado sigue siendo el mismo. email5 sigue siendo un Email, no se convierte en otra cosa [3:15].
¿Qué es un side effect en programación? Es una acción complementaria que se ejecuta junto a otra operación principal, como un log, una notificación o el envío de datos, sin modificar el objeto sobre el que se aplica.
¿Cómo combinar let, apply, also y run en una sola función?
Las scope functions por separado son útiles, pero juntas se vuelven realmente potentes. Veamos un ejercicio: crear una función validateEmail que reciba un String nulable y retorne un Boolean.
Los pasos son:
- Usar
let para verificar que el correo no sea nulo.
- Usar
apply para limpiar espacios sobrantes con trim.
- Usar
also para imprimir el correo ya limpio.
- Usar
run para validar y retornar el resultado.
Una posible solución es:
kotlin
fun validateEmail(email: String?): Boolean {
return email?.let { emailString ->
emailString.apply { trim() }
.also { println("Correo limpio: $it") }
.run { contains("@") && contains(".") }
} ?: false
}
¿Por qué se usa el operador Elvis al final?
Porque toda la expresión encadenada puede retornar null si el email original es nulo. Como la firma exige un Boolean, el operador ?: te permite definir un valor por defecto, en este caso false, para mantener la coherencia del tipo de retorno [6:30].
Fíjate en algo importante: dentro de una scope function puedes aplicar otra sobre su parámetro o su contexto. Por eso apply, also y run viven anidados dentro del let. El valor que retorna run es el que termina propagándose hacia afuera, porque let también devuelve la última expresión de su bloque.
Habilidades y conceptos clave de esta clase
- Scope function with: se invoca como función independiente con el objeto como argumento, ideal para múltiples operaciones sobre el mismo objeto [0:24].
- Retorno de la última expresión: tanto
with, run como let devuelven el resultado de la última línea de su bloque.
- Scope function also: ejecuta side effects sin alterar el tipo ni el valor del objeto asignado [3:00].
- Acceso con it: dentro de
also el objeto se referencia como it, no como this.
- Anidamiento de scope functions: puedes encadenar
let, apply, also y run para construir flujos expresivos [5:45].
- Operador Elvis (?:): define un valor por defecto cuando una expresión nulable podría retornar
null [6:50].
- Función trim: método de
String que elimina espacios sobrantes al inicio y final.
¿Ya tienes claro en qué escenario usarías with y en cuál also? Cuéntame en los comentarios cómo los aplicarías en tu propio proyecto.