Por que o Jepsen foi escrito em Clojure
(aphyr.com)- O Jepsen é uma ferramenta para verificar sistemas concorrentes como bancos de dados, então o modelo de concorrência da linguagem de implementação e o suporte a threads reais eram critérios importantes na escolha
- As estruturas de dados imutáveis e persistentes do Clojure e as ferramentas de concorrência da JVM reduzem a chance de erros no código de teste, além de combinarem bem com clientes de bancos de dados baseados em Java
- O trabalho no Jepsen se aproxima mais de experimentação e exploração do que de desenvolvimento de produto com escopo fixo, então recursos do Clojure como REPL, macros, threading macro e EDN eram vantajosos para lidar com processamento de dados complexo
- O desempenho não está entre os melhores, mas Clojure idiomático costuma ser apenas um ou dois dígitos mais lento que Java, e os pontos necessários podem ser tratados com otimização e ferramentas de profiling da JVM
- Há fraquezas como comunidade pequena, ausência de um sistema de tipos estáticos amplamente bem-sucedido, desconforto ao lidar com primitives e mensagens de erro, mas para o Jepsen, mantido e usado por 1 a 3 pessoas, esse era um compromisso aceitável
Requisitos de linguagem adequados para uma ferramenta de teste de concorrência
- Jepsen foi criado para testar sistemas concorrentes, e seus alvos de teste são, na maioria das vezes, bancos de dados
- Clojure atende bem aos requisitos básicos para escrever código de teste de concorrência
- As estruturas de dados imutáveis e persistentes reduzem a probabilidade de erros em código que lida com estado compartilhado
- É possível usar threads reais, promise, future, atom, lock, queue, cyclic barrier,
java.util.concurrentetc. - Linguagens com controle mais rígido de efeitos colaterais, como Haskell, também foram consideradas, mas a abordagem menos dogmática do Clojure se encaixou melhor no Jepsen
- Testes de bancos de dados exigem conexão com vários clientes
- Quase todo banco de dados normalmente oferece um cliente JVM escrito em Java
- Clojure tem boa interoperabilidade com Java, o que facilita aproveitar esses clientes
Recursos do Clojure vantajosos para experimentação e exploração de dados
- Como os testes do Jepsen são um trabalho experimental, era necessária uma linguagem concisa, fácil de mudar e adequada para prototipagem
- A sintaxe concisa e o sistema de macros do Clojure combinam bem com esse modo de trabalho
- O threading macro torna transformações encadeadas mais fáceis de ler
- Macros facilitam implementar tratamento de erros reutilizável e controle de escopo de recursos
- O REPL do Clojure é útil para explorar na hora os dados gerados pela execução dos testes
- Durante os testes, é preciso representar, transformar e inspecionar estruturas de dados complexas e aninhadas
- As estruturas de dados do Clojure e as funções da biblioteca padrão são fortes nesse tipo de tarefa
- Ao imprimir muitos dados estruturados no console e em arquivos, a sintaxe de dados EDN se encaixa bem
O compromisso entre desempenho e manutenção de longo prazo
- O Jepsen não é um sistema enorme, mas lida com uma quantidade razoável de dados, então precisa de desempenho “bom o bastante”
- Clojure não é a linguagem mais rápida
- Clojure idiomático normalmente fica dentro de uma diferença de um ou dois dígitos em relação ao Java
- Em gargalos importantes, é possível reduzir essa diferença
- As ferramentas de profiling da JVM funcionam bem com Clojure
- O Jepsen é um projeto com cerca de 10 anos, então um núcleo maduro e estabilidade eram importantes
- Clojure é estável tanto como linguagem quanto como alvo da JVM
- As bibliotecas não “apodrecem” tão rápido quanto em Scala ou Ruby
Fraquezas do Clojure que precisam ser aceitas
- O Clojure tem desvantagens que, na escala do Jepsen, são administráveis, mas podem pesar em equipes maiores
- A comunidade de engenharia é pequena
- Não há um sistema de tipos estáticos amplamente aceito e bem-sucedido
- Como o Jepsen é mantido e usado por apenas 1 a 3 pessoas por vez, essa limitação pesa menos
- Ao lidar com primitives da JVM, pode ser frustrante se não descer para Java, e na prática Java é usado às vezes
- Partes do sistema de polimorfismo são insuficientes, mas isso pode ser contornado com bibliotecas, e as mensagens de erro também não são boas
- O Jepsen foi prototipado em várias linguagens antes da decisão por Clojure, e mesmo depois de 10 anos isso continua sendo um compromisso bastante bom
1 comentários
Comentários no Hacker News
Como alguém que mantém um SaaS em Clojure/ClojureScript há uns 10 anos, concordo totalmente com o texto do Kyle
Além disso, algo de grande valor foi poder colocar a maior parte do código de domínio em arquivos cljc para compilar tanto para o servidor quanto para o cliente, o fato de transducers e pipelines de transducers serem subestimados em termos de desempenho, combinabilidade e reutilização, e a excelente estabilidade e compatibilidade de longo prazo, como se espera de uma linguagem projetada por Rich Hickey
Compatibilidade retroativa é muito importante, então dá para focar na aplicação em vez de ficar à mercê de ferramentas que mudam o tempo todo, e também não acho coincidência que o programador médio de Clojure tenha mais de 12 anos de experiência e receba remuneração de quartil superior
Quase nada quebra e atualizar é fácil. Graças a HMR, REPL e arquivos cljc, a experiência de desenvolvimento também é excelente, e escrever código em Clojure é divertido
Dizem que tudo bem dobrar linguagens e sistemas para trás para não quebrar o que já funciona, mas não entendo por que se espera que justamente essas duas linguagens, que sustentam uma parte enorme do software que realmente roda, cortem seus laços com um passado e um presente brilhantes
O maior problema do Clojure é com certeza o ecossistema
Quando se olha a distribuição das ferramentas acessíveis e utilizáveis, parece que ou elas estão na extrema esquerda, sendo horríveis mas fáceis de acessar, ou na extrema direita, com citações de O Senhor dos Anéis no README e tudo sendo dado até a enésima potência, acessíveis apenas a dragões capazes de escrever plugins de Emacs vendados e pendurados de cabeça para baixo
A linguagem em si é excelente e a interoperabilidade com Java também é muito melhor do que o Aphyr disse, mas se Clojure tivesse algo como Rails ou Django, ou melhor ainda, Phoenix, seria bem mais fácil de defender e provavelmente teria sido muito mais adotado
Especialmente para convencer uma equipe a sair de uma linguagem imperativa da família Algol e ir para Lisp, o ganho precisa superar o esforço, mas hoje, tirando elementos básicos como roteamento HTTP, em quase todo projeto é preciso reinventar a roda
Ainda assim, poder usar qualquer pacote Maven e compilar para jar é uma vantagem enorme: onde um jar roda, Clojure também roda
https://calva.io
Clojure existe em várias formas, e principalmente Clojure e ClojureScript são usados, com Babashka, Scittle etc. também existindo, mas todos derivados de clj ou cljs
As ferramentas de desenvolvimento variam conforme a plataforma e a preferência de IDE, então Emacs, Neovim, VSCode e IntelliJ têm ferramentas diferentes, e o sistema de build também muda conforme a necessidade
Em clj,
deps.edné hoje o mais usado, seguido de leiningen, e em cljs há várias opções além de shadow-cljsPara quem está começando isso pode ser esmagador, mas a comunidade é boa e sempre ajuda. Já programei profissionalmente em várias linguagens, mas Clojure é de longe a minha favorita
Uso Clojure desde por volta de 2013 e não me vem nenhuma ferramenta assim à cabeça. A maioria das bibliotecas populares é excelente e está muito à frente da maioria dos ecossistemas que conheço em estabilidade, compatibilidade retroativa e simplicidade
Graças aos arquivos-fonte .cljc, que podem ser compartilhados entre Clojure e ClojureScript, o mesmo código Clojure pode rodar tanto no frontend quanto no backend
Como Rich Hickey diz, não é só a JVM que ele “alcança”, mas também o JavaScript
Uso Emacs há muito tempo e consigo configurá-lo como quero ou acabar escrevendo o elisp de que preciso, mas não diria de forma alguma que sei bem elisp
Reconheço que, para alguém que nunca aprendeu nenhum dialeto Lisp, aprender Emacs, elisp e Clojure ao mesmo tempo é difícil e muito complicado de vender
Hoje existe o servidor LSP de Clojure, que funciona não só no Emacs, mas também em outras IDEs, então, embora muitos desenvolvedores de Clojure usem Emacs, ele não é obrigatório para desenvolver em Clojure
Estou usando só Clojure há 7 anos e trabalho ativamente em uma base de código Clojure + ClojureScript com mais de 500 mil linhas, e minha maior reclamação é a falta de um sistema de tipos robusto
Concordo que Clojure é expressiva, permite avançar rápido e não atrapalha, mas também permite erros de runtime confusos que são difíceis de reproduzir e depurar
Se existisse um sistema de tipos estático amplamente aceito e bem-sucedido, Clojure seria incrível. Hoje vejo como uma opção bem razoável se você gosta de Ruby ou Python, mas quer algo mais forte, e agora começo a me perguntar se Go é o caminho certo
Clojure(Script) é realmente uma bênção, e concordo totalmente que é uma excelente escolha para muitos projetos, pequenos ou grandes
Depois de usar um pouco de C, Python, PowerShell, Java, Bash e SQL em trabalhos da faculdade, administração de sistemas e análise de dumps de tráfego, a primeira linguagem que usei profissionalmente foi Clojure
Estou criando o OrgPad com Clojure+ClojureScript e, mesmo com mais de 100 mil linhas de código, continuo achando que foi uma boa decisão
É fácil de abordar e, depois de escrever um pouco de código, tenho bastante confiança de que ele vai funcionar como imaginei. Em Python, PowerShell e Bash não senti isso nesse nível; parece uma categoria completamente diferente
O fluxo de trabalho com REPL também é muito bom, e um colega fez um vídeo sobre isso: https://www.youtube.com/watch?v=4igO7Qbyj9o
Hoje em dia, até funções, tarefas curtas e pequenos projetos que fariam você pensar em migrar de Bash para Python podem ser resolvidos com Babashka/nbb
Quando tentei escrever alguns milhares de linhas em Clojure e ClojureScript no passado, o maior problema era que a maioria das ferramentas tinha estados de erro bem bagunçados
Python também tem algo parecido, então se você interromper a maioria dos programas Python com Ctrl+C, aparece um stack trace misturado com coisas indecifráveis, mas para quem está começando isso dificultava seguir em frente
Acho que a linguagem e o ecossistema têm muitas ideias inovadoras e interessantes. No mínimo, vale a pena ler o artigo histórico do Clojure e internalizar a maior parte das lições
Isso vai soar herético para todo mundo, mas acho que existe espaço no ecossistema para uma linguagem de script que seja tão séria quanto Clojure em desempenho e ergonomia de escrita de código, mas sem parênteses
Também dispensaria as convenções de tratar
nilde forma especial, embora pelo menos exista alguma base para issoEu sei que
(f x y)é melhor do quef(x, y), mas meu problema é mais quando formasletacabam induzindo indentação. Não acho que deveria ser necessário pagar ao mesmo tempo o custo de indentação e de parênteses só para dar nome a um valorQuando você vem de uma linguagem como Python, que de qualquer forma usa a indentação do código como informação, às vezes essas coisas parecem apenas ruído
letcriar indentação é mais um sinal de que você está usandoletdemaisEm estilo funcional, muitas vezes não é necessário nomear argumentos intermediários passados entre funções
Em geral, usa-se macros de threading como
->e->>para passar implicitamente o argumento para a próxima função, ou então define-se a própria transformação como uma função pura e a coloca-se no topoAinda assim, entendo que em algum código de interoperabilidade seja preciso muito
let. Especialmente com bibliotecas Java de JWT que modificam argumentos em cadeias proceduraisTipo passar um tempão depurando e depois perceber que não recarregou ou reinicializou algo
Minha sensação é que, na maioria das vezes, isso só entra para a lista de coisas a verificar quando você pensa “isso não pode estar acontecendo”. Por exemplo: “vou colocar uma saída de debug para confirmar se minha mudança de código está mesmo sendo chamada”
Claro, isso é só uma rede de segurança, e normalmente tento evitar esse tipo de situação com outros hábitos de trabalho no REPL
(+ clojure clojurescript)Clojure acerta muita coisa, e por isso gosto muito dela
Quando uma equipe real constrói trabalho web real em Clojure, acho que uma coisa que desacelera é o desejo quase dogmático de juntar inúmeras bibliotecas, cada uma à sua maneira, e costurar tudo
Se a comunidade tivesse, além da abordagem atual, também um framework web ativamente mantido, com baterias incluídas e geralmente utilizável, acho que Clojure seria muito mais popular e mais agradável de usar. Fico curioso se o Biff cumpre esse papel
Seria bom haver um caminho alternativo para quem gosta da linguagem, mas não gosta dessa forma de montar um monstro com as próprias mãos
Talvez seja por eu ter sido condicionado pelo Ruby on Rails
É a abordagem do famoso talk do Rich Hickey: não complect, compose: (https://github.com/matthiasn/talk-transcripts/blob/master/Hi...)
Aprender Clojure, um banco de dados em grafo e, em alguns casos, até HTMX já é bastante coisa. Mesmo assim, muita configuração já vem pronta, a autenticação é cuidadosa e fácil, e os comentários no arquivo de configuração do servidor são bem escritos
Também há scripts de deploy para configurar um servidor Linux e entrar por SSH no REPL de produção. Se você quiser usar XTDB, o jeito como o Biff faz validação de documentos é realmente muito bom
Pessoalmente, gosto mais disso do que de apps rodando em ClojureScript. Existem muitos wrappers de ClojureScript em volta do React e isso quase virou meme, mas como desenvolvedor React nunca achei nenhum deles mais fácil que o próprio React
Pelo contrário, parecem mais complexos e menos naturais de usar. Já HTMX/Hyperscript combinam muito bem com Clojure, porque a sintaxe de HTML não passa de arrays e objetos fáceis de manipular
Foi a primeira vez que o mantra repetido por entusiastas de Clojure, “código é dado”, pareceu realmente concreto
Antigamente, tentei usar OCaml várias vezes, aos poucos, mas não gostei do sistema de tipos excessivamente rígido
Eu passava mais tempo satisfazendo o compilador do que escrevendo código. Em Clojure, Common Lisp e C, há muito mais liberdade para dobrar o compilador ou o sistema de tipos na direção que eu quero
Claro que há prós e contras, mas eu não quero ser atrapalhado enquanto escrevo código. Pode ser só o meu estilo, mas conheci muita gente que pensa da mesma forma
Acredito que sistemas de tipos estáticos são uma questão de preferência, e o lado oposto pensa diferente. Ainda assim, é bom haver campos opostos, porque eles aprendem muito um com o outro
Rich Hickey já disse várias vezes que, como Clojure é uma linguagem hospedeira, não há problema em descer para Java ou JavaScript quando necessário
Pessoalmente, vejo isso como um dos recursos mais fortes do ecossistema Clojure. É muito mais prático
Já vi muitas tentativas de reimplementar repetidamente a mesma coisa na linguagem preferida só porque a pessoa não quer tocar em “aquela biblioteca suja escrita em outra linguagem”. Os motivos variam: é lenta, não é segura, não é flexível etc.
Isso acontecia com frequência em Common Lisp e na comunidade Rust, e continua acontecendo hoje
O SBCL garante essas declarações tanto quanto possível e provavelmente já me poupou alguns minutos de dor de cabeça por projeto
E CL é muito prático, então, por exemplo, ninguém nem pisca porque o woo é baseado em uma biblioteca C
Todos os problemas operacionais estavam ligados a questões pequenas e sutis. Um upgrade de runtime mudava a ordem das chaves no JSON e quebrava clientes antigos que faziam parsing manual de JSON, ou alguma biblioteca no fundo da stack passava a retornar códigos HTTP como inteiros em vez de strings, quebrando o parsing de erros
Naquela escala operacional, mesmo que só uma pequena porcentagem dos clientes fosse afetada, isso já gerava centenas de relatos de problema, então toda falha era grave
Agora trabalho em uma startup pequena, e é completamente diferente. Escolhemos uma linguagem estática porque era para onde iam a atenção e as pessoas, mas sinto falta da época em que trabalhava com linguagens dinâmicas
Como precisamos lançar rápido, a qualidade do código realmente piora. Por falta de tempo, em vez de lidar com o sistema de tipos e refatorar tudo de forma limpa, copiamos várias estruturas de objeto parecidas, mas ligeiramente diferentes, e as usamos em vários lugares
Por causa do formato dos tipos, generalizar fica difícil, então, em vez de separar a funcionalidade e referenciá-la em vários pontos, acabamos copiando/colando a mesma lógica em 10 lugares
Acho que a solução certa depende do domínio do problema. Se você está recebendo chamadas no meio da noite por causa de bugs sutis, provavelmente tipagem estática rígida é a escolha certa
Se você precisa iterar rápido e o problema pode ser resolvido com um e-mail ou uma mensagem no Slack, tipagem dinâmica pode ser melhor
O truque é não acumular dívida demais no paradigma dinâmico, pensando no momento em que você crescer e precisar migrar para um paradigma estático
Infelizmente, linguagens com muitos fãs apaixonados parecem tender com facilidade para a atitude de “não quero usar bibliotecas de outras linguagens”
Clojure é de longe a minha linguagem favorita e parece natural para mim
Ainda assim, há duas coisas de que sinto muita falta em outras linguagens: mensagens de erro compreensíveis e dicas de tipo
Tive de refatorar um código grande no trabalho, e foi doloroso. É difícil saber se uma função vai lançar uma exceção ou simplesmente retornar nil, e também é difícil saber se uma função central vai retornar um vetor, uma sequência ou outra coleção qualquer
Existem projetos como spec, malli e typed clojure tentando resolver isso, mas não pareceram substitutos próximos dos tipos de Python ou TypeScript
Uma linguagem que não consegue dizer o que entra e o que sai de uma função parece ter quase nenhum valor em um codebase que passe de alguns arquivos
Tentar inferir tipos olhando para o que o código atual aparentemente consegue fazer com um objeto é pedir para sofrer
Se você está conhecendo Clojure agora, vale a pena conferir este tutorial interativo: https://tryclojure.org
É um conjunto de aprendizado interativo melhor, que vai do nível fácil ao intermediário e ao difícil
Clojure é divertido
Rails dominou o desenvolvimento web nos anos 2010, e muitas startups enormemente bem-sucedidas foram construídas sobre ele
Muita gente zomba dizendo que engenheiros estão “brincando com brinquedos”, mas uma boa parte das coisas boas que temos hoje já foi brinquedo um dia. Linux é um ótimo exemplo
A parte complicada de uma linguagem muito “divertida” é que você passa a presumir que pode resolver tudo nela
No fim, você escreve muito código, e depois percebe que talvez não escrever código já fosse uma solução boa o bastante
[1] https://franz.hamburg/writing/clojure-makes-happy.html