Por que Clojure?
“Um simbiótico Lisp para programação funcional, com uma plataforma estabelecida, projetado para concorrência.” — Rich Hickey
Mais uma linguagem de programação? Sério?
Aprender uma nova linguagem de programação exige tempo, esforço e muita dedicação. Então, por que aprender mais uma? E por que escolher Clojure dentre as diversas opções existentes?
Para responder à estas perguntas, vou apresentar algumas das motivações por trás da criação do Clojure, adicionando alguns motivos pessoais que considero importante para justificar a escolha pela linguagem.
Programação Funcional e Imutabilidade
Clojure é uma linguagem de propósito geral, dinamicamente tipada e predominantemente funcional. Isto significa que ela utiliza funções como base para a realização de operações computacionais, evitando, na medida do possível, alterações de estado e dados mutáveis.
Existem diversos artigos que explicam muito bem os fundamentos da programação funcional. Sendo assim, ao invés de explicar a teoria por trás do paradigma, vou manter o foco em demonstrar aplicações práticas em Clojure em meus próximos artigos, sem deixar de mencionar os principais pontos da linguagem.
No que diz respeito à gerenciamento de estado e mutabilidade, a linguagem oferece um conjunto de estrutura de dados persistentes e imutáveis que facilitam na construção de softwares mais robustos e menos suscetíveis a falhas, principalmente em aplicações que necessitam de processamento concorrente.
Simplicidade
Lisp (List Processing), é uma família de linguagens de programação que se baseia em um modelo computacional chamado Cálculo Lambda. Clojure, por ser um dialeto de Lisp, herda as características inerentes desse tipo de linguagem, como por exemplo:
- Notação prefixada com parênteses: Uma soma de dois números, por exemplo, é representada desta forma
(+ 1 2)
, ao invés da notação infixa utilizada na aritmética,1 + 2
; - Expressões aninhadas: Uma soma seguida de uma multiplicação é representada desta forma
(* 3 (+ 1 2))
, ao invés de3 * (1 + 2)
. Caso adicionássemos mais uma operação de subtração, nossa expressão ficaria desta forma(- 2 (* 3 (+ 1 2)))
, e o Clojure continuaria a avaliação das expressões de "dentro pra fora" (entendeu a "Lisp Joke" agora?). Apesar de inicialmente isto não parecer nem um pouco atrativo, existem algumas vantagens por trás deste modelo; - "Code-as-data" e "homoiconicity": A sintaxe de um Lisp é construída inteiramente a partir de estruturas de dados que a própria linguagem pode gerar e manipular. Sendo assim, a leitura de um código fonte de um Lisp é essencialmente a sua própria representação interna;
- Suporte a macros: a característica anterior é, e não somente é, a base do sistema de macros presente nos Lisps, que permite a extensão da linguagem. Em resumo, macros permitem adicionar funcionalidades que não estão presentes por padrão na linguagem, justamente por esta tratar código como dado. No mínimo interessante, não?
Essas características fazem com que a linguagem praticamente não possua sintaxe, seja extremamente expressiva, concisa e simples (simples, não fácil). Isso ficará ainda mais claro quando colocarmos a mão na massa :)
Plataforma
Clojure foi criado para ser uma hosted language. Atualmente, é possível compilar código Clojure para as seguintes plataformas:
- Java Virtual Machine (plataforma padrão);
- Javascript (Clojurescript);
- CLR (.Net Framework);
- Código nativo utilizando GraalVM;
- Entre outras plataformas não-oficiais, como por exemplo o Clojurerl (Erlang).
Esta característica permite a utilização do vasto ecossistema de bibliotecas existente nas plataformas citadas através da interoperabilidade de código e também, no proveito da estabilidade e maturidade que estas plataformas possuem. Caso queira saber mais sobre este assunto, recomendo a leitura deste excelente artigo.
Outro ponto importante, é que isso facilita na portabilidade e compartilhamento de código. É possível criar uma aplicação web inteira utilizando apenas Clojure + Clojurescript, podendo até compartilhar funções que não sejam restritas à suas plataformas específicas.
REPL
Se você já programa em Python ou Ruby, com certeza sabe das vantagens de se ter um REPL em mãos. Para os que não conhecem, o REPL (Read Eval Print Loop), é uma ferramenta que permite que vc interaja diretamente com a linguagem enquanto desenvolve, avaliando o código, printando, criando loops e etc, sem precisar de fato compilar e executar o código para fazer alguma experimentação ou testar se seu código se comporta como deveria.
Geralmente, programadores Clojure seguem a estratégia de desenvolvimento orientado a REPL para receber feedbacks constantes durante a construção de seus códigos, melhorando a sua produtividade drasticamente.
Concorrência
Estamos próximos do limite físico da computação. O número de cores disponíveis nos processadores tem aumentado, justamente para lidarmos com a necessidade de realizar operações computacionais mais rapidamente. Porém, por motivos óbvios, esta é uma solução que não pode escalar indefinidamente.
“It can’t continue forever. The nature of exponentials is that you push them out and eventually disaster happens.” — Gordon Moore, April 2015
Sendo assim, os sistemas atuais precisam mais do que nunca aproveitar ao máximo o poder dos processadores multi-core. Existem diversas formas de se alcançar este objetivo utilizando paralelismo, concorrência, multi-thread, assincronismo e etc. Porém, todas estas estratégias podem trazer algumas implicações, além do aumento significativo na complexidade do código, como por ex:
- Sincronização e coordenação;
- Acesso simultâneo à estruturas compartilhadas;
- Deadlocks;
- Race conditions;
- Starvation e Etc.
Muitas linguagens de programação como Python, Ruby e Java, foram criadas em um cenário single thread. Clojure teve sua primeira aparição em 2007, tendo concorrência como um dos pontos principais no design da linguagem.
O resultado disso é, uma linguagem projetada para simplificar o desenvolvimento de softwares concorrentes, diminuindo drasticamente a suscetibilidade à falhas que este cenário proporciona.
Como as estruturas de dados nativas da linguagem são imutáveis, é possível compartilha-las entre múltiplas threads, sem o risco de um comportamento indesejado de alteração incorreta de valores, já que… elas são imutáveis!
Quando há a necessidade de alteração de estado, Clojure oferece mecanismos de transação e retentativa que garantem que, ao realizar uma alteração de estado, a consistência seja mantida, sem a necessidade de nenhum código de bloqueio explícito por parte do desenvolvedor (locks, mutex e etc).
Em resumo, o que a linguagem oferece nativamente, é um ambiente de construção de código concorrente, simples, performático e seguro. E este é outro assunto que exige um artigo só pra ele.
Meu ponto de vista
Penso que aprender uma linguagem focada em programação funcional, sendo um Lisp, lhe trará uma nova maneira de enxergar os problemas, como desenvolvedor.
"Linguagens de programação nos pedem para remodelar nossas mentes, e isso as torna profundamente pessoais e subjetivas." — Ramsey Nasser
Por experiência própria, após aprender Clojure e programação funcional, pude perceber uma melhora significativa em meus códigos, tanto na organização, qualidade e redução de “tentativa e erro”, como na forma de resolver problemas computacionais. Além disso, a forte influência da matemática na programação funcional, só colabora para o desenvolvimento de códigos mais eficientes.
No final do dia, linguagens de programação são ferramentas, um meio para um fim. Muitos desenvolvedores vão simplesmente optar por aprender linguagens populares que lhe garantam um salário no final do mês. E não há nenhum problema nisso. Se bem que nesse ponto, Clojure não está se saindo mal:
Porém, aqueles que se interessam em não apenas aprender a programar em uma linguagem nova ou seguir o hype, mas em entender a motivação por trás dos diversos paradigmas de programação, em enxergar computação através de novas óticas, em aumentar o seu leque de conhecimento, evoluindo pessoal e profissionalmente, com certeza acharão Clojure uma ótima opção :)
Próximos passos
Enfim, chegamos ao final deste meu primeiro artigo.
Caso esteja interessado em aprender mais sobre Clojure, acompanhe minha série de artigos práticos sobre a linguagem:
- Parte 1: Conhecendo os building blocks da linguagem;
- Parte 2: Polimorfismo, Multi-methods, Protocols e Records;
- Parte 3: Concorrência, paralelismo e gerenciamento de estado.
Não esqueça de deixar a sua dúvida, crítica ou sugestão nos comentários. Minha intenção com este artigo, é fortalecer a comunidade de Clojure no Brasil e ajudar o máximo de pessoas que tenham interesse em aprender a linguagem. Então, toda ajuda é bem vinda :)
Até logo!