2 pontos por GN⁺ 2023-12-07 | 1 comentários | Compartilhar no WhatsApp
  • 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.concurrent etc.
    • 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

 
GN⁺ 2023-12-07
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

    • Situação parecida. Meu SaaS também roda em Clojure, e a manutenção é absurdamente fácil em comparação com outras linguagens
      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
    • Do total de vantagens, que parte você acha que vem da JVM e que parte vem da própria linguagem Clojure?
    • Existe algum framework web realmente bom para Clojure?
    • Se compatibilidade retroativa é tão importante assim, fico curioso por que isso é tratado como exceção só quando se aplica a C e C++
      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

    • Vale muito a pena conhecer o Calva, um excelente plugin para VSCode. Ele oferece não só REPL, mas também recursos como documentação em tempo real, então fica fácil usar Clojure, e eu uso isso todos os dias no trabalho
      https://calva.io
    • Não parece que você já tenha programado em Clojure profissionalmente. O ecossistema e a comunidade de Clojure são bem mantidos
      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-cljs
      Para 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
    • Fiquei curioso com exemplos de ferramentas “horríveis mas fáceis de acessar”
      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
    • Isso parece mais um comentário aleatório sobre Clojure do que algo relacionado a Jepsen ou ao texto principal
    • Para quem não conhece bem Clojure, vale acrescentar que, como /u/jwr disse nesta thread, Clojure também roda onde houver navegador graças ao ClojureScript
      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

    • Nas partes em que tipos estáticos ajudam de forma mensurável, é possível integrar outras linguagens da JVM como Kotlin a projetos em Clojure
  • 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 nil de forma especial, embora pelo menos exista alguma base para isso
    Eu sei que (f x y) é melhor do que f(x, y), mas meu problema é mais quando formas let acabam 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 valor
    Quando 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

    • O fato de a forma let criar indentação é mais um sinal de que você está usando let demais
      Em 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 topo
      Ainda assim, entendo que em algum código de interoperabilidade seja preciso muito let. Especialmente com bibliotecas Java de JWT que modificam argumentos em cadeias procedurais
    • Ao usar o fluxo de trabalho com REPL em Clojure ou Python, há coisas que você aprende por engano
      Tipo 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
    • Meu cérebro sofre porque parece que isso deveria ser lido como (+ 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

    • Para muita gente, o apelo do Clojure está justamente em não amarrar coisas desnecessariamente
      É a abordagem do famoso talk do Rich Hickey: não complect, compose: (https://github.com/matthiasn/talk-transcripts/blob/master/Hi...)
    • O Biff chega bem perto de vir com baterias incluídas, mas alguns padrões dele fogem um pouco do mainstream. Usa XTDB em vez de SQL, e HTMX/Hyperscript, então ainda pode ser difícil para quem está chegando
      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
    • Como eu já escrevi acima, vale a pena conferir Luminus ou Kit
    • Das coisas que usei, o Biff é o que mais me agrada
  • 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

    • Verificação de tipos realmente é uma questão de preferência. Pessoalmente, gosto bastante quando a linguagem impede erros triviais, então coloco muitas declarações de tipo em código Common Lisp
      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
    • Quando eu liderava uma equipe de alta confiabilidade em Big Tech, eu queria colocar tipagem estática rígida em tudo já no dia anterior
      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

    • Pode ser porque me acostumei com linguagens modernas que têm esses recursos, ou porque linguagens que não os têm estão tentando adicioná-los depois, como Python/TypeScript, mas fico me perguntando como as pessoas aguentam isso
      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

    • https://4clojure.oxal.org/
      É um conjunto de aprendizado interativo melhor, que vai do nível fácil ao intermediário e ao difícil
  • Clojure é divertido

    • Isso é subestimado. Pode soar pouco profissional, mas a diversão também é um dos motivos de Basecamp e Ruby on Rails terem sido feitos em Ruby
      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
    • Essa expressão captura perfeitamente a sensação. Já escrevi antes sobre esse sentimento [1]
      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