16 pontos por GN⁺ 2025-02-25 | 3 comentários | Compartilhar no WhatsApp
  • Clojure não é uma das linguagens de programação mainstream e pode ser desconhecida para algumas pessoas
  • Principais vantagens de Clojure
    • Produtividade do desenvolvedor: Clojure é interativa, exige menos trabalho repetitivo e oferece um ambiente de desenvolvimento eficiente. Os desenvolvedores conseguem lançar produtos rapidamente com satisfação
    • Manutenibilidade de longo prazo: a linguagem Clojure e seu ecossistema são maduros e estáveis. É possível construir sistemas de alta qualidade reduzindo os custos de manutenção
    • Cultura centrada em ideias: a comunidade Clojure explora ideias do passado e do presente, da academia e da indústria, em busca de formas melhores de desenvolver software. Isso oferece aos desenvolvedores novos desafios e oportunidades de aprendizado

(hello 'clojure)

  • Clojure é uma das linguagens da família Lisp, criada originalmente nos anos 1950
    • Lisp começou como um modelo teórico, mas também oferece grande elegância conceitual na programação prática
    • Como a sintaxe do código coincide com a própria estrutura de dados, ela traz várias vantagens em relação a linguagens com gramáticas mais complexas
    • Foi usada como linguagem principal durante o antigo boom de IA
  • As linguagens da família Lisp ganharam popularidade e depois desapareceram em ciclos, mas nos últimos 10 anos Clojure voltou a chamar atenção
  • Clojure roda sobre a JVM do Java e incorpora conceitos modernos de programação
    • Suporte poderoso a estruturas de dados imutáveis
    • Projeto pensado para concorrência
  • Embora tenha crescido sem o apoio de grandes empresas, em uma comunidade pequena, possui características linguísticas muito fortes
  • Clojure pode ser executada em vários ambientes
    • ClojureScript: compila para JavaScript
    • ClojureCLR: roda em ambiente .NET
    • Babashka: interpretador de scripting rápido com GraalVM
    • Jank: suporte a compilação nativa
  • Ao aprender Clojure, é possível aproveitar o mesmo conhecimento em vários ambientes

Desenvolvimento interativo

  • Programar é um processo repetitivo de escrever e validar código
    • Sem feedback, é difícil ter certeza de que o código funciona corretamente
    • Formas comuns de feedback:
      • Executar scripts repetidamente
      • Interação com UI e saída de logs
      • Uso de testes unitários
      • Uso do compilador e de ferramentas de análise estática
    • Feedback rápido tem grande impacto na produtividade do desenvolvedor
      • Quanto mais lento o feedback, mais tempo é gasto com debugging e menos tempo sobra para escrever código
  • Além dessas formas tradicionais, Clojure oferece desenvolvimento interativo (interactive development)
    • O núcleo disso é o desenvolvimento baseado em REPL (Read-Eval-Print Loop)
    • Antes de escrever o código, o desenvolvedor executa o runtime do Clojure e o conecta ao editor
    • É possível executar trechos de código um a um e receber feedback imediato
    • Graças à estrutura sintática consistente do Lisp, é possível executar livremente desde pequenos trechos até blocos maiores de código
  • Diferentemente de um REPL comum, Clojure permite executar trechos de código enquanto está conectada a um sistema em execução
    • Isso oferece um loop de feedback muito mais poderoso do que o fluxo tradicional de "escrever código → executar → depurar"
    • É possível modificar e depurar o programa em tempo real
    • Não é apenas um REPL simples, mas uma poderosa ferramenta de interação utilizável até em ambientes de produção

Cultura que valoriza estabilidade

  • Ao escolher Clojure, você não adquire apenas uma tecnologia poderosa; torna-se também parte de uma comunidade com filosofia e princípios próprios
    • Se adotar apenas a tecnologia sem interagir com a comunidade, será difícil experimentar o verdadeiro valor de Clojure
  • A comunidade Clojure considera estabilidade e compatibilidade retroativa valores fundamentais
  • A linguagem principal continua sendo aprimorada e expandida quase sem mudanças incompatíveis
  • O ecossistema open source de Clojure segue a mesma filosofia e minimiza mudanças desnecessárias (churn)
    • Isso contrasta com a maioria dos ecossistemas modernos de linguagens de programação
    • O desperdício de recursos causado por mudanças desnecessárias chega à casa de bilhões de dólares no mundo todo
  • Em Clojure, atualizar para as versões mais recentes da linguagem e das bibliotecas é algo muito natural
    • É possível aproveitar correções de bugs, atualizações de segurança e melhorias de desempenho sem precisar reescrever a base de código
    • Em outras linguagens, código antigo pode quebrar quando sai uma nova versão; em Clojure, ele tende a continuar funcionando
  • Alguns desenvolvedores podem se perguntar: "como evoluir sem mudanças?"
    • Porém, estabilidade não é o mesmo que estagnação
    • Clojure evolui adicionando e melhorando recursos sem quebrar os já existentes
    • Assim, os desenvolvedores podem usar ferramentas cada vez melhores sem fazer alterações desnecessárias no código

Sistemas de informação e representação do conhecimento

  • No desenvolvimento de aplicações web e de negócios, coletar, acessar e processar informação é o ponto central
  • No entanto, muitas linguagens mainstream apresentam designs ineficientes para representar e manipular informação
    • Forçam o uso de estruturas de dados de baixo nível, criando complexidade desnecessária
    • Sistemas de tipos estáticos podem ser rígidos demais, dificultando manipulações flexíveis de dados
  • Clojure oferece por padrão estruturas de dados funcionais e poderosos recursos de manipulação de dados
    • Como linguagem de tipagem dinâmica, segue a "Open World Assumption"
      • Uma abordagem que maximiza extensibilidade e flexibilidade dos dados
    • Sofre forte influência de RDF (framework de modelagem de dados para a Semantic Web)
      • Um exemplo representativo é a forte sinergia com bancos de dados em grafo como Datomic
      • Com keywords com namespace, é possível atribuir significado independente de contexto
  • A estrutura de mapas (Map) do Clojure com keywords com namespace permite expressar significados de forma mais sofisticada do que um JSON simples
  • Também facilita a expansão dos dados e permite representação intuitiva sem colisões de nomes

Funções pequenas e combináveis, e dados imutáveis

  • Em Clojure, é comum programar com foco em funções puras (pure functions) e dados imutáveis (immutable data)
  • Dá para escrever código imperativo no estilo Java, Ruby ou C, mas o Clojure idiomático é bem diferente
    • Funções puras: retornam resultados com base apenas nos valores de entrada e não alteram estado externo
    • Dados imutáveis: têm significado baseado em valor (value), não em referência (reference) nem identidade de objeto (identity)
  • Como não dependem de estado externo, o comportamento do código é mais previsível
  • Não há alterações em variáveis globais nem efeitos colaterais inesperados (side effects)
  • Como não existe risco de os dados serem alterados, fica mais fácil resolver problemas de paralelismo e concorrência

Tratamento de concorrência

  • A computação moderna se baseia em processadores multicore, e concorrência é um elemento essencial
    • Com os limites da lei de Moore, aproveitar paralelismo é a chave para ganhos de desempenho
    • Mas, ao lidar com estado mutável (mutable state), surgem problemas de sincronização e controle complexo de timing
  • Clojure enfatiza a imutabilidade (immutability) para resolver problemas de concorrência pela raiz
    • Manipular memória mutável cria dependência de ordem e timing
    • Transformações puras de dados (data-in, data-out) sempre podem ser executadas em paralelo com segurança
  • Clojure aproveita os recursos de concorrência já existentes na JVM (java.util.concurrent), mas oferece ferramentas abstraídas de nível mais alto
    • Atoms: suporte a operações atômicas com CAS (Compare-and-Set) e retry automático
    • Refs: oferecem Software Transactional Memory (STM)
    • Agents: aplicam atualizações de forma assíncrona
    • Futures: oferecem interface fork-and-join baseada em thread pool
  • Também fornece a biblioteca core.async
    • Suporte ao padrão CSP (Communicating Sequential Processes), parecido com as goroutines do Go
    • Comparável ao modelo Actor de Erlang/Elixir e ao Akka do Scala
  • Se necessário, também é possível usar técnicas de controle de concorrência de nível mais baixo
    • Filas sincronizadas, referências atômicas, locks, semáforos, vários tipos de thread pool, gerenciamento manual de threads etc.
  • Quando preciso, há controle fino de concorrência, mas na maioria dos casos usar ferramentas mais abstratas é mais seguro e eficiente

Raciocínio local (Local Reasoning)

  • Há um limite para a complexidade de código que conseguimos considerar de uma vez
  • Quando o estado e as mudanças de um programa acontecem em muitos lugares, entender e manter o código se torna difícil
  • Por que Clojure facilita o raciocínio local
    • Código centrado em funções puras (pure function)
      • É possível entender completamente o comportamento de uma função apenas pelos valores de entrada
      • Não é necessário considerar estado externo à função
    • Diferença em relação às linguagens orientadas a objetos
      • Clojure minimiza o polimorfismo, tornando mais fácil identificar onde o código está sendo executado
      • Na programação orientada a objetos (OOP), "tudo acontece em outro lugar",
        enquanto em Clojure basta rastrear as funções definidas no namespace
  • A estrutura sintática consistente de Clojure também facilita refatoração
    • Como usa dados imutáveis e funções puras, mudanças no código geram menos efeitos colaterais inesperados
    • Separando à parte apenas o mínimo de código imperativo, é possível combinar harmonicamente código imperativo e funcional

Testes fáceis

  • Em código Clojure baseado em funções puras, basta fornecer valores de entrada e verificar os de saída para testar
    • Não é preciso inicialização complexa de estado, configuração de mocks nem controle de timing
    • Por isso, os testes são mais confiáveis e há menos flakiness (falhas inconsistentes nos testes)
  • Há suporte a técnicas avançadas como Property Based Testing (Generative Testing)
    • Gera entradas aleatórias para procurar violações de propriedades específicas ou invariantes
    • Também oferece técnicas de shrinking para encontrar o menor caso de falha
  • Esse conceito começou no Haskell e foi implementado em várias linguagens como frameworks de teste inspirados no QuickCheck
  • Em Clojure, isso funciona ainda melhor em sinergia com estruturas de dados imutáveis e desenvolvimento orientado a REPL

Vantagens na contratação de desenvolvedores Clojure

  • Em geral, há menos desenvolvedores Clojure do que em linguagens populares como JavaScript e Python
  • Porém, também não são tantas as empresas que usam Clojure
    • Por isso, o equilíbrio geral entre oferta e demanda se mantém
    • Na prática, empresas que procuram desenvolvedores Clojure e profissionais dessa área frequentemente conseguem se encontrar de forma adequada
  • Desenvolvedores Clojure costumam ter alto nível de resolução de problemas
    • Em vez de aprender apenas tecnologias populares, muitos são profissionais que exploram novas formas de pensar e novas ideias
    • Empresas que usam Clojure frequentemente avaliam que, embora haja menos candidatos, a qualidade média é alta
    • Exemplo: Nubank treinou diretamente centenas de desenvolvedores em Clojure no Brasil e deixou um caso de sucesso
  • Encontrar desenvolvedores Clojure não é simples, mas quando a empresa encontra o perfil certo, a chance de contratar ótimos profissionais é alta
    • Em vez de buscar apenas pessoas com experiência prévia na linguagem, também vale formar desenvolvedores com grande capacidade de aprendizado
    • A própria comunidade Clojure tende a atrair pessoas que pensam profundamente sobre problemas e buscam resolvê-los

Trade-offs e ajuste do nível de abstração

  • Clojure é uma linguagem high-level, voltada a código conciso e expressivo
    • Graças a estruturas de dados funcionais (imutáveis) e APIs poderosas de manipulação de dados, muita complexidade desnecessária desaparece
  • Para garantir imutabilidade, as estruturas de dados de Clojure usam internamente árvores (Hash Array Mapped Trie)
    • Em atualizações, ocorre path copying, o que pode aumentar a carga do GC (garbage collection)
    • No processo de interoperabilidade com Java, podem ocorrer runtime reflection e boxing/unboxing
    • Em aplicações comuns, esse custo costuma ser quase irrelevante, enquanto o ganho de produtividade é grande
  • Em casos que exigem alto desempenho, como engines gráficas em tempo real, processamento de sinais ou computação numérica, é possível fazer otimizações de baixo nível
    • Clojure oferece type hints para eliminar reflection e otimizar operações com tipos primitivos
    • Dá para aproveitar cache de CPU com arrays contíguos de tipos primitivos
    • Bibliotecas com aceleração por GPU também podem ser usadas
  • Na maioria das linguagens high-level, quando desempenho importa é preciso recorrer a extensões nativas em C/Rust, mas
    Clojure consegue resolver a maior parte dos problemas de desempenho aproveitando as otimizações da JVM (compilação JIT)
    • Com profiling e um pouco de otimização, é possível extrair o máximo de desempenho de componentes como event loops

Metaprogramação e APIs orientadas a dados

  • Como linguagem da família Lisp, Clojure trata código como dados
    • De forma parecida com JSON, mas com uma estrutura mais legível, é possível representar programas
    • Também utiliza o formato de dados EDN (Extensible Data Notation), que oferece uma representação semelhante ao JSON
  • Clojure oferece recursos de transformação do próprio código por meio de macros
    • Porém, a comunidade Clojure tem uma cultura de uso criterioso e restrito de macros
    • Macros podem dificultar debugging e ter menor compatibilidade com ferramentas de análise estática
  • Em vez disso, prefere-se o design de APIs orientadas a dados (data-driven)
    • Exemplos: roteamento HTTP, geração de HTML/CSS, validação de dados etc.
    • Em vez de chamar diretamente funções específicas, o comportamento é descrito com estruturas de dados (mapas, vetores)
    • APIs baseadas em dados podem ser manipuladas dinamicamente e facilitam salvar e alterar configurações de usuário
  • Graças a essas APIs orientadas a dados, é possível reconfigurar sistemas dinamicamente em runtime
    • Isso facilita implementar sistemas de simulação altamente flexíveis, gestão dinâmica de configuração e metaprogramação

Interoperabilidade com Java (Interop) e uso do ecossistema

  • O desenvolvimento moderno de aplicações é um processo de combinar inúmeras bibliotecas open source e APIs
    • Como Clojure roda na JVM, é possível aproveitar milhões de pacotes Java disponíveis no Maven Central
    • Também é possível chamar bibliotecas Java de forma simples sem depender de reflection
  • Em comparação com Java, Clojure tem código muito mais conciso e facilita programação experimental com REPL
    • Dá para explorar e combinar APIs muito mais rapidamente do que em Java
    • Com ClojureScript, é possível aproveitar da mesma maneira JavaScript e o ecossistema NPM

Cultura centrada em ideias

  • É possível ter projetos bem-sucedidos com qualquer linguagem e, da mesma forma, usar qualquer linguagem de forma errada e fracassar
    • Uma boa ferramenta não cria, por si só, bons desenvolvedores
  • Clojure tem curva de entrada alta e exige tentativa e erro no aprendizado, mas isso também leva a pensamento mais profundo
  • Alguns projetos em Clojure ainda carregam estilos de código herdados de Java ou Python e acabam sem aproveitar bem a linguagem
    • Porém, equipes que adotam a filosofia e as ideias de Clojure desenvolvem melhor capacidade de projetar software
  • A comunidade Clojure questiona continuamente as formas existentes de desenvolver e busca maneiras melhores
    • As palestras de Rich Hickey (criador do Clojure) não são apenas apresentações técnicas, mas uma exploração de princípios fundamentais de design de software
    • Em conferências de Clojure, o foco costuma estar mais em ideias, análise de papers e compartilhamento de experiência do que em apresentar bibliotecas

Conclusão

  • Clojure não é apenas uma linguagem de programação, mas uma comunidade de pessoas que pensam em formas melhores de desenvolver software
  • Nessa comunidade, ampliar os próprios limites e explorar novas possibilidades é um valor central
  • Desenvolvedores que crescem nessa cultura não apenas dominam Clojure, mas se tornam engenheiros de software com maior capacidade de resolver problemas

3 comentários

 
roryk 2025-02-26

Pessoalmente, uso Clojure e concordo bastante com o conteúdo do texto.
No trabalho, tenho usado principalmente Python e Java(Type)Script, mas bastava relaxar um pouco na manutenção para acabar sendo difícil acompanhar as mudanças da própria linguagem e das bibliotecas, e o código logo virava legado. No caso de Clojure, fiquei muito satisfeito com o fato de que, mesmo ao rever um código escrito há um ano, é muito fácil partir direto para ajustes e desenvolvimento.
Desde então, para uso pessoal, tenho preferido Clojure sempre que não há limitações de alguma biblioteca específica.

 
plumpmath 2025-02-25

Por que Clojure?

Jank Jank~!

 
GN⁺ 2025-02-25
Comentários do Hacker News
  • Se me perguntarem de qual tipo de programação eu mais gostei, foi construir pipelines para processar dados no shell e escrever Clojure e ClojureScript nos últimos 5 anos

    • Atualmente, 4 pessoas estão escrevendo Clojure juntas e, no total, têm mais de 30 anos de experiência com Clojure
    • Começaram uma empresa de consultoria em Praga que prefere Clojure e acham que pode ser uma boa escolha para vários sistemas
  • Uso Clojure há 12 anos e, antes disso, usei Java por mais de 12 anos

    • Criei ótimos apps e bibliotecas com Clojure e Java
    • Descreve Clojure como "a linguagem de programação menos ruim", com um núcleo poderoso e estável
    • Quando se domina as ferramentas, a velocidade e a precisão no trabalho melhoram
    • O fluxo de trabalho orientado a REPL do Clojure é uma proposta de valor importante
  • Amo escrever Clojure e percebi que não preciso explicar meu profundo apreço por Clojure nem mesmo ao compará-lo com outras linguagens

    • Não me interesso por problemas pessoais com outras linguagens, e meu amor por Clojure vem de razões teóricas, práticas, emocionais e financeiras
    • Clojure não é perfeito, mas é a linguagem de programação de uso geral mais satisfatória
  • O cofundador tem como objetivo criar o máximo de produto com o mínimo de empresa

    • Fizeram bootstrap por 5 anos e alcançaram mais de US$ 1 milhão em ARR, e Clojure teve um papel importante nisso
    • Clojure também contribuiu para a felicidade como programador, e há planos de expandir a equipe principal de produto em Clojure no futuro
  • Mantive um negócio SaaS por 10 anos usando Clojure, e isso não teria sido possível sem Clojure

    • A estabilidade da linguagem é extremamente útil, e em outros ecossistemas muitas vezes é preciso reescrever software com frequência
    • Recomenda ler posts de pessoas que realmente usaram a linguagem, em vez de críticas superficiais
  • Recomenda <a href="https://www.flow-storm.org/">Flow Storm</a> para quem usa Clojure

    • Junto com uma boa suíte de testes, é possível acionar vários cenários
  • Aprendi muito com Rich Hickey e tinha paixão por Clojure e FP

    • Porém, à medida que o negócio cresceu, a tipagem dinâmica virou um fardo, e depois que pessoas-chave saíram ficou difícil encontrar desenvolvedores Clojure
    • Hoje, a maior parte da stack é composta por Python e Go
  • Houve um apontamento de que a documentação do ClojureDocs está desatualizada, e havia vontade de adicionar um recurso para votar nas respostas

    • Graças ao algoritmo do Google, o ClojureDocs continua sendo o principal recurso usado
  • A parte sobre a estabilidade do Clojure foi surpreendente, e a sensação era de que, a cada tentativa anual, tudo tinha mudado

    • Pergunta se o "Hello World" ainda é lento para rodar, e diz que ler Clojure é agradável, mas escrevê-lo sempre foi uma barreira
  • Depois de começar com Common Lisp, migrou para Go e Rust, mas recentemente voltou a olhar para Clojure

    • Graças ao REPL e à programação interativa, dá para fazer as coisas rapidamente
    • A JVM é um ótimo runtime