Funções em Clojure
Tags: clojure, functional
Published: 2021-01-08 18:04:47

Vimos no [post anterior](https://dev.to/guilhermelo/comecando-com-clojure-4939) como são as estruturas básicas de dados em Clojure. Nesse post, veremos como declarar e trabalhar com funções na linguagem Clojure.

# Definindo funções

A criação de uma função é dividida em cinco partes, sendo elas:

1. defn
2. Nome da função
3. A documentação da função, sendo opcional
4. Parâmetros que são passados entre colchetes
5. Corpo da função

Criando nossa primeira função em Clojure:

```
(defn ola
"Função que retorna um texto de boas vindas!"
[nome]
(println "Olá, " nome))
```

Acima definimos o nome da nossa função como "ola", escrevemos uma documentação simples, passamos "nome" como parâmetro e criamos um corpo da função que imprime a frase retornada.

Invocando a função:
```
(ola "Guilherme")
; Olá Guilherme
```

# Parâmetros e Aridade
Em linguagens orientadas a objetos podemos ter métodos com o mesmo nome, porém com quantidade de parâmetros diferentes na mesma classe. Em Clojure, temos algo parecido. Podemos definir que uma função pode ter diferentes formas de implementação com diferentes aridades (número de parâmetros passados para a função), o que chamamos de sobrecarga de aridade (arity overloading). Abaixo nossa função "ola" ganha novas aridades e para isso, devemos modificar um pouco a forma como escrevemos a função, envolvendo cada forma de implementar com parênteses.
```
(defn ola
"Função que retorna um texto de boas vindas!"
([nome]
(println "Olá," nome))

([nome sobrenome]
(println "Olá," nome sobrenome))

([nome sobrenome idade]
(println "Olá," nome sobrenome ". Idade:" idade "anos")))
```

Invocando a função utilizando as três formas:

```
(ola "Guilherme")
; Olá, Guilherme

(ola "Guilherme" "Rodrigues")
; Olá, Guilherme Rodrigues

(ola "Guilherme" "Rodrigues" 23)
; Olá, Guilherme Rodrigues . Idade: 23 anos
```

Clojure também nos permite definir uma função com aridade variável através do parâmetro rest (rest parameter). Para indicar que estamos utilizando rest parameter, devemos utilizar o simbolo & como no exemplo abaixo.
```
(defn imprime-nome [nome]
    (println "Olá" nome))

(defn ola [& nomes]
    (map imprime-nome nomes))
```

No exemplo acima definimos uma função chamada **imprime-nome** passando como parâmetro o nome. Utilizamos a função **imprime-nome** para mapear a lista de argumentos e imprimi-los. Invocando a função com os parâmetros:

```
(ola "Guilherme" "João" "José")

; Olá Guilherme
; Olá João
; Olá José
```

Para utilizar rest parameter juntamente com outro parâmetro, devemos passa-los como último parâmetro da função. Exemplo:

```
(defn mostra-time [time & nomes]
    (println "Os jogadores do" time "são: " 
        (clojure.string/join ", " nomes)))
Invocando a função:

(mostra-time "Real Madrid" "Benzema" "Casemiro" "Rodrigo")
; Os jogadores do Real Madrid são: Benzema, Casemiro, Rodrigo
```

# Desestruturação
Clojure nos permite extrair valores dos parâmetros que são passados para a função utilizando um conceito chamado destructuring. No exemplo abaixo recuperamos apenas o primeiro elemento da coleção passada como parâmetro e retornamos o mesmo.
```
(defn primeiro-elemento [[primeiro-elemento]]
    primeiro-elemento)

(primeiro-elemento ["Carro" "Bicicleta" "Caminhão"])
; "Carro"
```

Clojure nos permite utilizar a desestruturação com rest parameters como no exemplo abaixo:

```
(defn dois-primeiros-elementos-e-resto [[primeiro segundo & resto]]
    (println "Primeiro:" primeiro)
    (println "Segundo:" segundo)
    (println "Outros:")
    (println (clojure.string/join ", " resto)))

(dois-primeiros-elementos-e-resto ["Carro" "Bicicleta" "Caminhão" "Metrô" "Ônibus"])

; Primeiro: Carro
; Segundo: Bicicleta
; Outros:
; Caminhão, Metrô, Ônibus
```

Também é possível utilizar desestruturação com mapas. No exemplo abaixo os valores das chaves :nome e :sobrenome informadas são passados para os parâmetros nome e sobrenome:

```
(defn ola [{nome :nome sobrenome :sobrenome}]
(println "Nome:" nome)
(println "Sobrenome:" sobrenome))

(ola {:nome "Guilherme" :sobrenome "Rodrigues"})

; Nome: Guilherme
; Sobrenome: Rodrigues
```

# Corpo de função
O corpo da função pode conter inúmeras formas dentro dele. Porém, Clojure retorna apenas a última expressão avaliada pela função. No exemplo abaixo temos diversas formas, porém apenas a última é retornada:

```
(defn corpo-funcao []
    (+ 10 15)
    "Corpo da função"
    (* 100 12))

(corpo-funcao)
; 1200
```

# Funções anônimas
Acima fizemos um exemplo utilizando a função **map** e passamos a função **imprime-nome** como parâmetro. Relembrando a função:

```
(defn imprime-nome [nome]
    (println "Olá" nome))

(defn ola [& nomes]
    (map imprime-nome nomes))
```

Porém, em Clojure não é necessário que todas as funções tenham nomes, pois a linguagem nos possibilita utilizar funções anônimas. Vejamos abaixo como podemos utilizar funções anônimas nesse caso:
```
(defn ola [& nomes]
    (map (fn [nome] 
           (println "Olá" nome)) nomes))

(ola "Guilherme" "João")         
    
; Olá Guilherme
; Olá João
```

Como visto, para definir uma função anônima, trocamos o **defn** por **fn**, retiramos o nome da função e passamos como parâmetro de outra função. Porém, podemos diminuir mais um pouco a definição da função anônima. Para isso, deixamos de utilizar o **fn**, não definimos o nome do parâmetro e substituimos o parâmetro apenas por %. Exemplo acima com função encurtada:

```
(defn ola [& nomes]
    (map #(println "Olá" %) nomes))

(ola "Guilherme" "João")    
                 
; Olá Guilherme
; Olá João
```

Caso a função anônima receba mais de um parâmetro, é possível utiliza-los como no exemplo abaixo:

```
(#(+ %1 %2) 10 15)
;25
```
    
Espero que tenha gostado das funções em Clojure. Por enquanto é só e nos vemos em um futuro post. Abraços :)