Clojure é um dialeto da linguagem de programação funcional Lisp, tendo como principal característica a utilização de estruturas de dados imutáveis. Ao mesmo tempo, o seu sistema de tipos é totalmente dinâmico e o time de desenvolvimento está se esforçando para implementar uma tipagem mais forte.
Na indústria, temos muitos casos de sucesso do Clojure. Empresas americanas como Walmart e Puppet Labs adotaram a linguagem de programação para a construção de seus sistemas internos. No Brasil, ficou nacionalmente conhecido por ser usado na arquitetura de microsserviços da Nubank, chegando em até 90% do código escrito.
Separamos os principais tópicos para entender sobre a linguagem, então confira com a gente:
- O que é Clojure?
- Para que Clojure é usado?
- Por que Clojure é popular?
- Quais as principais características da Clojure?
- Quais as diferenças entre Clojure, Scheme/Racket e Common Lisp?
- Por que programadores devem ficar de olho em Clojure?
- Quais as vantagens e desvantagens da linguagem Clojure?
- O que a Clojure tem a ver com o Nubank?
- Instalação do Clojure
- Primeiros passos: Hello World em Clojure
O que é Clojure?
Clojure é uma linguagem de programação funcional que usa como base a JVM (máquina virtual Java). É inspirada totalmente no Lisp e é considerada como um dialeto de Lisp. O seu desenvolvimento atual é feito com a ajuda da comunidade e supervisionado por Rich Hickey.
Defendendo a imutabilidade e as estruturas de dados imutáveis, Clojure incentiva os programadores e programadoras a serem explícitos sobre o gerenciamento das suas informações e estados. Com o foco na programação com valores imutáveis e funções matemáticas, conseguimos criar programas mais robustos.
Da mesma forma que outros dialetos de Lisp trata códigos como dados e tem um sistema de macro integrado, Clojure também consegue exercer isso e na prática conseguimos criar um novo macro que corresponde a adicionar novas construções à linguagem. Portanto, é possível alterar de acordo com a necessidade do projeto.
História
Disponível desde 2007, a linguagem foi criada por Rich Hickey e toda lógica roda sobre a JVM (Máquina virtual do Java), mas temos a possibilidade de compilar o código para outras plataformas, utilizando bibliotecas como ClojureScript que compila para Javascript e CLR que traduz para a plataforma .NET.
A linguagem foi criada com o objetivo de resolver problemas que o Rich Hickey estava tendo na hora de escrever alguns programas situacionais. Independentemente da linguagem de programação que ele usava, o resultado foi sempre igual — softwares que exigiam uma quantidade monumental de tempo e esforço para apenas fazer alterações relativamente pequenas na codificação.
O projeto foi um sucesso, toda sua equipe gostou de trabalhar dentro de uma estrutura que tinha dados imutáveis e uma biblioteca funcional personalizada. Ao combinar a linguagem de programação funcional que era Lisp e rodá-la em uma JVM como o Java foi projetado, Clojure nasceu.
Programação funcional
Simplificando, a programação funcional trata da construção de funções para variáveis imutáveis. Em comparação, a programação orientada a objetos é sobre ter um conjunto relativamente fixo de funções, e você está basicamente modificando ou adicionando novas variáveis.
Devido a forma que é, a programação funcional é ótima para tarefas sob demanda, como análise de dados e aprendizado de máquina. Isso não significa que você não vai mais utilizar programação orientada a objetos e apenas programação funcional. O paradigma é muito útil, no entanto, devemos entender os conceitos básicos para que possamos utilizá-lo ao nosso favor.
Para que Clojure é usado?
Muitas características do Lisp foram reutilizadas em outras linguagens, mas há duas coisas que ainda permanecem distintas no Lisp, a utilização de código como dados e um sistema de macro único.
Clojure é um pouco diferente, pois estende o código como sistema de dados além dos termos listados entre parênteses (s-expressões), e em maps e vetores. Fazendo isso, os maps e vetores podem utilizar a sintaxe do macro e ter as representações literais do leitor.
Os dados do Lisp são lidos pelo leitor e representam a estrutura de dados em todas as formas. Clojure se baseia nisso compilando essas estruturas de dados que representam o código, como parte do processo que chama macros. Logo após o Clojure executar isso, a macro é chamada para passar os próprios formulários como argumentos e, em seguida, usa o valor do retorno da macro como um substituto para a própria macro.
As macros são métodos que são chamados em tempo de compilação para realizar as transformações do próprio código. Como todos os dados são códigos e todos os códigos são dados, toda a biblioteca Clojure possibilita a transformação do código.
Resumindo, você usa as macros da mesma forma que usa funções nas outras linguagens para eliminar a repetição de código. Em ocasiões onde os métodos não conseguem dar comandos para realizar uma determinada tarefa, uma macro consegue fazer operações como avaliação de controle e gerar identificadores definidos pelo usuário.
Por que Clojure é popular?
A forma que as estruturas de dados são imutáveis no Clojure é um ponto forte da linguagem. A partir disso, conseguimos adicionar uma nova chave em uma estrutura map e ele retornará um novo ao invés de modificar o valor antigo, enquanto que a performance da linguagem é executada tão bem como as outras linguagens.
Isso acaba se tornando muito poderoso na hora de escrever programas multithread. Um dos grandes problemas com o multithreading é a sincronização de acesso aos recursos – o que pode levar a um deadlock, onde o programa não é capaz de acessar os recursos necessários para a continuação da operação.
O problema com deadlock não ocorre no ambiente do Clojure. Quando utilizamos estruturas de dados imutáveis, temos que os dados não podem ser alterados enquanto estão sendo acessados. Caso ocorra uma leitura durante uma gravação, Clojure consegue tratar essa situação separando os tipos de referência, rotulando as alterações e os valores alterados para o reajuste posterior.
Quais as principais características da Clojure?
Já sabemos que o Clojure é uma linguagem funcional. No paradigma funcional temos a enfatização das avaliações de funções (como funções matemáticas) e evitamos alterações do estado, diferencialmente da programação imperativa por exemplo, em que temos o incentivo a alteração de estados, no paradigma funcional a função é a unidade básica do código, contrário ao paradigma OOP onde entendemos que a unidade básica são os objetos.
A seguir veremos algumas das principais características do Clojure.
Imutabilidade
Como comentado, não existe alteração de estado no paradigma funcional e no Clojure não poderia ser diferente. Temos no Clojure um conjunto de listas, vetores, sets e maps, todos imutáveis. Já que eles não conseguem adicionar ou remover nenhum valor dessas estruturas de dados, acabamos criando uma nova coleção com os valores antigos e o recente valor.
Outra vantagem da imutabilidade é a questão de testes. Contamos com um número bem menor de estados e transições para testar. Pela lógica, um sistema imutável é muito mais simples para testar totalmente.
Um dos problemas que o Java enfrenta, é quando um objeto é alterado, para tentar resolver isso temos sempre uma cópia da referência desse objeto. No Clojure já é bem diferente. Já que todos os estados são imutáveis, conseguimos compartilhar qualquer referência de objeto pois sabemos que não teremos problemas de alteração em nenhum objeto interno. O Clojure utiliza várias técnicas para salvar a memória, sendo uma das mais famosas a flyweight pattern.
High-Order function
Uma High-Order function é uma função que ou aceita uma ou mais funções como argumento, ou retorna uma função como resultado. Esse é um dos conceitos fundamentais da programação funcional em qualquer linguagem.
Essas funções nos permitem criar outras funções, ou seja, conseguimos escrever pequenas funções que são combinadas para criar funções maiores. Seria como colocar um monte de pequenos blocos de LEGO juntos para a construção de uma casa.
First-Class functions
Funções podem ser tratadas como valores na programação funcional. Dessa forma, conseguimos atribuir a valores que são passados para uma função, e que também são retornados por uma função.
As First-Class functions são essenciais para o estilo de programação funcional. Um exemplo disso é a função map, que espera receber no argumento uma função e uma lista, e retorna uma lista com o valores aplicados em cada membro da lista. Para que uma linguagem suporte o map, ela deve suportar a passagem de funções como argumento. Além disso, no Javascript também utilizamos a função map.
Quais as diferenças entre Clojure, Scheme/Racket e Common Lisp?
Como qualquer linguagem que segue um paradigma, observamos uma série de diferenças entre as linguagens de programação, veremos abaixo as principais diferenças entre as maiores linguagens funcionais.
Características do Common Lisp
- Um sistema de OOP muito forte
- É uma das que tem compilador mais rápido.
Características do Clojure
- O maior ecossistema de bibliotecas, já que podemos utilizar todas as classes do Java.
- Maior ênfase na imutabilidade e programação funcional assíncrona, algo inspirado na linguagem Haskell.
- Fortes recursos para a concorrência e um amplo suporte de memória transacional de software no nível de máquina.
Características do Scheme/Racket
- Indiscutivelmente o mais simples e fácil de aprender.
- Temos as Hygienic macros, que evitam elegantemente os problemas com a captura acidental de símbolos nas expansões de macro.
Por que programadores devem ficar de olho em Clojure?
Como já vimos, Clojure foi criado pelo Rich Hickey como uma tentativa de resolver alguns problemas que ele estava tendo na hora de escrever alguns programas. Em geral, não importava a linguagem, ele sempre acabava com um software que exigia muito esforço para fazer mudanças relativamente pequenas. Essa situação não é muito diferente para as diversas pessoas programadoras.
Em outro projeto, ele decidiu utilizar um método mais imperativo e tentou fazer as coisas de forma diferente. Utilizando estruturas de dados imutáveis e uma biblioteca funcional que foi criada de maneira personalizada, sua equipe teve uma experiência muito mais agradável, tornando o projeto um sucesso.
Programar de maneira a funcionar é muito bom, mas aplicar esse paradigma em qualquer linguagem que não é funcional, acaba se tornando muito complicado. Dessa maneira, Rich e sua equipe resolveram criar uma nova linguagem para isso. Vamos dar uma olhada nos pontos da programação funcional que podem ser interessantes para novos programadores e programadoras.
REPL
Alguns ambientes de execução oferecem um REPL (Read, Evaluate, Print, Loop) como parte do kit que você está baixando. A ideia é que, quando você entra no modo REPL, você consiga enviar instruções de uma fonte externa e obtenha os resultados na tela assim que estiverem prontos.
No Clojure temos esse conceito em uma forma melhorada: nele você executa um REPL remoto e expõe um soquete do seu programa em execução (mesmo em um ambiente de produção). No fim, você pode interagir com o sistema ao vivo enquanto ele está instalado e funcionando, tornando muito prática a depuração.
Essa vantagem está presente também em outros ambientes de execução, porém, no momento, o Clojure é a única linguagem com essa abordagem.
Ecossistema
Para saber se uma linguagem de programação é realmente boa, acabamos vendo sempre as suas comunidades. Componentes e bibliotecas são uma parte essencial na hora de criar um projeto de sucesso, e Clojure não fica atrás nesse quesito.
Como desenvolvedor ou desenvolvedora Javascript, você pode estar acostumado com a ideia de que as dependências dos seus projetos devem ser sempre atualizadas, e isso pode acabar até virando rotina no seu trabalho. No Clojure não temos esse problema.
Olhar uma biblioteca que não é atualizada há muito tempo é bem comum, e isso não é um indicativo de que o projeto foi abandonado ou seu código é ruim. Em Clojure, as bibliotecas tendem a ter um escopo bem pequeno e quando o seu objetivo principal é alcançado, o projeto é concluído e a equipe segue em frente.
Se você usar Clojure, dificilmente vai ter que atualizar uma dependência.
Sem objetos
O estilo do Clojure rejeita completamente o modelo orientado a objetos e o considerado bem superestimado e que não é particularmente útil para os aplicativos que escrevemos atualmente (programas situacionais).
Em algumas conferências, Rich Hickey explica detalhadamente por que os objetos acabam não sendo uma boa ideia. E por isso, no Clojure você tem apenas dados e funções, e sim, você consegue criar programas muito sofisticados com um conjunto bem mais simples de ferramentas.
Quais as vantagens e desvantagens da linguagem Clojure?
É muito importante entender que tipo de linguagem você está utilizando, para isso separamos as principais vantagens e desvantagens do Clojure para você.
Vantagens
Clojure roda na JVM
Como já comentado, a utilização da JVM é uma base perfeita para o Clojure, tanto por ser muito estável como por ter um alto desempenho. Você consegue escrever o código uma vez e consegue utilizar no Mac, Linux e Windows de maneira bem simples.
Na JVM também temos o suporte à coleta de lixo de alta qualidade, tudo isso em um pacote padronizado, multithreading e simples. As macros do leitor também removem todos os colchetes e parênteses.
Não é Lisp
Clojure tem uma sintaxe consistente, nomes de operação e funções polimórficas. Dessa maneira, não existem nomes estranhos e ilegíveis ou versões específicas da mesma função como acontece no Lisp, afinal, muitos erros simples tornam a linguagem Lisp muito odiada.
Não falta nada
Como já percebemos, a estrutura de dados imutável é realmente incrível. Parece mais do que certo estruturar programas com um monte de funções pequenas e isoladas nas quais os dados nunca mudam. Pode ser verdade que um estado compartilhado deve ser ruim para a simultaneidade, mas a simplicidade e facilidade de testar acabam compensando.
Desvantagens
Sintaxe complicada
Sem nenhum mistério, a sintaxe do Clojure é bem difícil e não tem muitas formas de contornar isso. Adicionaram um número bem grande de novas palavras-chave e em certo momento temos a sensação de ser um pouco “clichê”, mesmo sendo muito flexível. Não existe uma regra rígida e rápida para manter uma proporção de um para um entre arquivos e namespaces, mas é altamente recomendável adquirir o hábito de seguir essa convenção.
Muito conteúdo
Ao contrário do Unix e do Python, onde o núcleo é pequeno e há uma abundância de bibliotecas (algo que a maioria dos programadores e programadoras preferem), Clojure tem um número muito grande de funções, com muitas fazendo coisas muito semelhantes, somente de uma maneira ligeiramente diferente. Pode passar a sensação de ser muita coisa para aprender e memorizar, mas devemos lembrar que hoje em dia temos a internet para nos ajudar nessas situações.
O que a Clojure tem a ver com o Nubank?
Basicamente tudo! Com milhares de clientes e utilizando Clojure em 90% do microsserviços, podemos ver o quão grande essa linguagem impacta na vida de tantas pessoas.
Nubank é uma das grandes novas empresas de tecnologia nacionalmente conhecidas. Dessa maneira, no começo de tudo, eles buscaram ferramentas que poderiam ajudar a resolver problemas realmente complexos, como o de remover a burocracia do sistema financeiro e devolver às pessoas o controle do seu dinheiro.
Atualmente, eles têm diversas bibliotecas internas feitas em Clojure, e, se eles precisam realizar cálculos de negócios ou de juros, podem apenas utilizar as suas próprias bibliotecas.
Além disso, a base do Nubank também conta com outras linguagens de programação, como Python e Scala, mas toda a arquitetura de microsserviços está escrita em Clojure.
Instalação do Clojure
Antes de instalar o Clojure, devemos ter o Java no nosso computador, já que ele será executado na JVM.
A instalação do Clojure no Windows, Linux e Mac deve ser feita por meio do Leiningen. Na seção Install temos os scripts para serem executados nos devidos sistemas operacionais.
No Windows, você deve baixar o script BAT e colocar em alguma pasta do seu sistema. Logo em seguida, na seção de variáveis de ambiente, você precisará adicionar o caminho para o executável na variável PATH.
Após isso, abra o terminal CMD e rode o comando lein -v, possivelmente vai mostrar uma mensagem de erro, então rode o comando lein self-install para instalar as dependências. No final disso tudo, rode novamente o primeiro o comando para visualizar a versão do Leiningen.
Primeiros passos: Hello World em Clojure
Vamos escrever o nosso primeiro código em Clojure. Como todo bom programador ou programadora, devemos escrever o famoso Hello World.
Com o Clojure já instalado na sua máquina, abra o seu terminal e vamos interagir com o comando lein repl.
A partir do user=> podemos escrever o nosso código.
Como estamos utilizando uma linguagem funcional, devemos sempre pensar em funções. Primeiramente vamos chamar a função println:
user=> (println “Hello World”)
Hello World
nil
user =>
Como pode ser visto, a partir da função println, passamos o texto que queremos imprimir no terminal “Hello World”. Na linha abaixo temos o texto e na sequência o retorno nil que significa que a função não retorna nada.
Sempre que quisermos utilizar uma função no Clojure temos que abrir um parênteses. Já que estamos usando uma função, conseguimos passar N parâmetros, por exemplo:
Dessa forma, entendemos o básico sobre a sintaxe do Clojure e conseguimos criar o nosso primeiro programa.
Conhecer novas linguagens é algo muito importante para um programador ou programadora. Além disso, entender o que cada linguagem propõe resolver é muito mais importante. Como pudemos ver, Clojure tenta solucionar diversos problemas com um passo-a-passo relativamente simples.
Com a utilização de estruturas de dados imutáveis e o entendimento que os métodos são apenas dados, tudo pode ficar muito confuso no começo, mas com a prática percebemos que tudo faz sentido e que acaba sendo até mais simples de realizar uma tarefa do que em outras linguagens.
A maneira que é escrita os programas funcionais é totalmente diferente dos programas imperativos ou orientados a objeto, mas não devemos pensar que as linguagens funcionais são piores ou melhores que as outras, e sim, devemos entender os problemas que elas podem resolver.
Esperamos que você tenha compreendido um pouco mais sobre o Clojure e o mundo da programação funcional, continue conosco!
Quer aprender mais sobre outras linguagens? Então leia sobre a Linguagem R: o que é, para que usar e por que aprender?