Funções recursivas e anônimas em Elixir
Tags: elixir, functional
Published: 2022-02-24 17:53:59

🏴󠁧󠁢󠁥󠁮󠁧󠁿 English version of this text [_here_](https://dev.to/lucassperez/recursive-anonymous-function-in-elixir-pn3)!

Um tempo atrás, eu tentei criar uma função recursiva e anônima em Elixir porque eu estava dentro do **iex** e estava com preguiça de criar um módulo dentro do _repl_. Minha primeira tentativa foi algo assim:

```elixir
# Fatorial ingênuo, anônimo e recursivo
fact = fn
  0 -> 1
  n -> n * fact.(n-1)
end
```

_OBS: Sim, é possível definir múltiplas cláusulas para funções anônimas!_

É claro que isso não funcionou, porque `fact/1` ainda não está definida no momento em que é chamada dentro do corpo da função.

Eu realmente não lembro _por que_ eu precisava de uma função anônima e recursiva, mas no começo dessa semana eu percebi o que que eu tinha que fazer para que funcionasse. Tudo o que temos que fazer é assegurar-nos que a função existe no momento em que é chamada, certo?

E se a função anônima recebesse, então, outra função como argumento, e essa função recebida fosse a que faz a chamada recursiva?

Agora o Elixir não poderia reclamar da função não estar definida, porque ela foi fornecida via um argumento, que poderia ser qualquer coisa:

```elixir
fact = fn
  0, _ -> 1
  n, recur -> n * recur.(n-1)
end
```

Bom, mas o que **é** essa função `recur`?

Ela poderia muito bem ser a própria `fact`! Somente temos que passá-la quanto a estivermos chamando:

```elixir
fact = fn
  0, _ -> 1
  n, recur -> n * recur.(n-1)
end

# Passe fact para si mesma, para que ela seja "recur" dentro do corpo da função
fact.(4, fact)
```

É claro que isso vai lançar uma exceção, pois agora **fact** tem uma aridade de **2**, e **recur** está sendo chamada com apenas um argumento. Lembre-se que **recur** é de "facto" **fact**! _Sim, tentei fazer um trocadilho._

Para resolver isso, tudo que temos que fazer é chamar **recur** passando **recur** para si mesmo, assim:

```elixir
fact = fn
  0, _ -> 1
  n, recur -> n * recur.(n-1, recur)
end

fact.(4, fact)

#=> 24
```

Também funciona com recursão de cauda, é claro. Tudo que teríamos que fazer é gerenciar os parâmetros e retornos corretamente.

**Legal, posso usar isso num Enum.map ou alguma outra função de ordem superior?**

Bom, somente se tomarmos cuidado.

Se escrevermos isso:

```elixir
fact = fn
  0, _ -> 1
  n, recur -> n * recur.(n-1, recur)
end

Enum.map([1,2,3,4], fact)
```

Então **Enum.map/2** tentará invocar `fact` com um argumento apenas, lançando uma exceção.

Uma maneira de resolver isso seria encapsulando-a dentro de uma outra função:

```elixir
fact = fn
  0, _ -> 1
  n, recur -> n * recur.(n-1, recur)
end

Enum.map([1,2,3,4], &(fact.(&1, fact)))

#=> [1, 2, 6, 24]
```

Outra maneira seria definir **fact** com uma interface mais legal.

A função **fact** poderia simplesmente receber um argumento e daí definir a função anônima e recursiva dentro de si mesma, e daí chamar a função anônima recém definida passando a mesma como argumento.

Seria algo assim:

```elixir
fact = fn n ->
  do_fact = fn
    0, _ -> 1
    n, recur -> n * recur.(n-1, recur)
  end

  do_fact.(n, do_fact)
end

Enum.map([1,2,3,4], fact)

#=> [1, 2, 6, 24]
```

Agora nós temos uma função recursiva e anônima que funciona como o esperado. Se eu quiser saber o fatorial de um número, apenas tenho que passar o número para a função, que é exatamente o que **Enum.map** tentará fazer.

Até definir na mesma linha essa função anônima funcionará:

```elixir
Enum.map([1,2,3,4], fn n ->
  do_fact = fn
    0, _ -> 1
    n, recur -> n * recur.(n-1, recur)
  end

  do_fact.(n, do_fact)
end)

#=> [1, 2, 6, 24]
```

E recursiva de cauda:

```elixir
Enum.map([1,2,3,4], fn n ->
  do_fact = fn
    0, acc, _ -> acc
    n, acc, recur -> recur.(n-1, n*acc, recur)
  end

  # Lembre-se de definir corretamente o valor inicial do acumulador. Aqui ele é 1
  do_fact.(n, 1, do_fact)
end)

#=> [1, 2, 6, 24]
```

Ter uma interface mais legal não é bom somente para usá-la como argumentos de uma função de order superior, também é mais legal para nós invocarmos essa função, é claro:

```elixir
fact = fn n ->
  do_fact = fn
    0, acc, _ -> 1
    n, acc, recur -> recur.(n-1, n*acc, recur)
  end

  do_fact.(n, 1, do_fact)
end

# Não há necessidade de passar nem o acumulador nem a própria função
fact.(4)
#=> 24
```

Agora se eu quiser saber o fatorial de 4, tenho que apenas chamar a função `fact/1` com **4** como argumento, sem ter essas coisas estranhas de passar a própria função por aí manualmente na chamada.

## Legal, mas isso é útil?

Para ser sincero, eu não sei.

Para começo de conversa, não é ilegal definir um módulo dentro do repl, então o cenário de "Estou com preguiça dentro do iex" não é tão importante assim. Quero dizer, eu tive que escrever tanta coisa a mais só para criar essa função recursiva e anônima que talvez simplesmente escrever `defmodule M do ... end` seja mais fácil e mais rápido.

Outra situação em que poderíamos usar isso seria em arquivos elixir normais onde temos algums **Enums** ou outras funções de ordem superior trabalhando, mas daí a gente provavelmente já vai estar dentro de um módulo, onde poderíamos definir uma função privada "com nome" (não anônima) para fazer esse trabalho e já dando um nome significativo a ela, aproveitando que vamos estar ali. Isso vai culminar num código melhor e mais legível do que a "solução" com as funções recursivas e anônimas malucas.

Mas ei, mesmo que isso não seja assim tão útil, pelo menos é sempre legal pensar sobre funções e recursão, certo?

E também foi bem divertido escrever esse post! :grin:

É isso por hoje. Obrigado por ler e tenha um bom dia! (: