Usando funções de escopo no Kotlin
Tags: kotlin, programming
Published: 2023-06-21 11:25:00

Se tem uma funcionalidade no Kotlin que é usada com muita frequência, mas muitas pessoas não tem ideia do que seja, são as [funções de escopo](https://kotlinlang.org/docs/scope-functions.html).

```kotlin
fun main() {
    val user: User? = findUserById(1)
    user?.let {
        println(it)
    }
}
```

Conseguiu identificar a função de escopo? Não? Não se preocupe que vamos entendê-las!

## Conhecendo as funções de escopo

Basicamente, função de escopo é uma função que executa um bloco de código dentro de um contexto de um objeto! Definição bem abstrata, né? Então vamos para a prática:
```kotlin
user.let {
    ...
}
```
Neste código, `let` é uma função de escopo que executa no contexto do `user` que é do tipo `User?`. Dentro deste contexto, temos acesso a `user` a partir do parâmetro da lambda, podemos até mesmo mudar o nome:

```kotlin
user.let { u ->
    println(u)
}
```

Usar funções de escopo dessa forma, não tem diferença de apenas chamar `println(user)`, concorda? Então porque elas existem? 

## Safe call com funções de escopo

Uma das maiores utilidades de funções de escopo é combinar com a [safe call](https://kotlinlang.org/docs/null-safety.html#safe-calls):

```kotlin
user?.let {
    println(it.name)
}
```

> Todo parâmetro único em expressões lambdas são subentendidos como `it` por padrão.

Essa abordagem tem o mesmo resultado de usar um `if` que garante que não é nulo. A diferença fica na sintax que é mais sucinta, permitindo até mesmo usar o `it` ou um nome que identifica melhor o contexto, como por exemplo, `nonNullUser`:

```kotlin
user?.let { nonNullUser ->
    println(nonNullUser.name)
}
```

## Variáveis apenas dentro do escopo esperado

Um outro caso que auxilia bastante, é que podemos trabalhar com variáveis que irão existir apenas nas funções de escopo. Se pegarmos a amostra inicial como exemplo, podemos criar a seguinte variação:

```kotlin
findUserById(1)?.let { user ->
    println(user.name)
}
```

Notou a diferença? Com essa abordagem, a existência da variável `user` é só dentro de `let`! Logo, você não precisa mais se preocupar com nullable ou criar uma variável desnecessária no resto da aplicação.

## Retornos em funções de escopo

Outro detalhe interessante, as funções de escopo podem retornar valores diferentes! Se usarmos o `let` como exemplo, ele sempre devolve o valor da última instrução, logo, se `println()` for a última instrução, será retornado `Unit`:

```kotlin
val result: Unit? = findUserById(1)?.let { user ->
    println(user.name)
}
```

Nesse caso em específico é `Unit?` por conta da safe call que sempre retorna um nullable! Caso modificarmos a última instrução, teremos um nullable diferente:

```kotlin
val result: Long? = findUserById(1)?.let { user ->
    println(user.name)
    user.id
}
```

Portanto, se atente aos retornos do `let` e de outras funções de escopo! 

## Função de escopo para configurar objetos durante a criação

Uma função de escopo bastante usada por conta do seu retorno, é a  `apply`. Um dos principais motivos é que ela **sempre retorna o objeto do contexto**.

Isso pode ser um pouco confuso, mas tem seus casos de uso. No artigo de [datas do Jetpack Compose](https://dev.to/alexfelipe/datas-no-jetpack-compose-com-date-picker-46a#:~:text=Formatando%20data%20em%20Long%20(milisegundos)%20para%20String), eu fiz a implementação de um formatador de datas utilizando a `apply`:


```kotlin
fun Long.toBrazilianDateFormat(
    pattern: String = "dd/MM/yyyy"
): String {
    val date = Date(this)
    val formatter = SimpleDateFormat(
        pattern, Locale("pt-br")
    ).apply {
        timeZone = TimeZone.getTimeZone("GMT")
    }
    return formatter.format(date)
}
```

Veja que ao criar o `SimpleDateFormat`, é feita uma configuração de `TimeZone` e o retorno é o próprio formatador criado! 

Em outras palavras, o `apply` serve para aplicar configurações extras após criação de um objeto. Logo, a variável retornada será criada com as configurações esperadas!

Inclusive, essa mesma implementação poderia ser mais sucinta e sem nenhuma variável:

```kotlin
fun Long.toBrazilianDateFormat(
    pattern: String = "dd/MM/yyyy"
): String = SimpleDateFormat(pattern, Locale("pt-br"))
    .apply {
        timeZone = TimeZone.getTimeZone("GMT")
    }.format(Date(this))
```

## Para saber mais

Esses são os casos mais comuns que utilizo funções de escopo no Kotlin, mas não para por aqui! Caso você quiser conhecer as demais funções como `run`, `also`, `with` etc, [recomendo que veja a documentação sobre funções de escopo](https://kotlinlang.org/docs/scope-functions.html).

Também, você pode aplicar outras técnicas, como chamadas encadeadas que permite usar mais de uma função de escopo em uma única chamada.

E ai, o que achou das funções de escopo do Kotlin? Você também usa no seu código? Aplica outras técnicas? Me conte aqui nos comentários 😄