Classificando Lisps: O Caso Clojure

Escrito em Ago 16, 2019 por Lucas Vieira
lucasvieira@lisp.com.br

Clojure é um dialeto moderno de Lisp que possui alta popularidade e grande uso na indústria. Este dialeto de Lisp utiliza sintaxe e ideias pouco ortodoxas com relação a outros dialetos mais conhecidos; estas diferenças são suficientes para que alguns membros da comunidade de dialetos Lisp questione se Clojure é, realmente, um dialeto de Lisp. Este artigo pretende expor meu ponto de vista com relação à linguagem, e também propor uma forma diferente de análise de dialetos Lisp, substituindo uma visão discreta (ser ou não ser um dialeto de Lisp) por uma visão descritível em uma escala, que relaciona a expressividade e a versatilidade dos Lisps tradicionais com a linguagem comparada. Proponho uma forma de ver Clojure sob esta ótica, e então mostro como uma comparação polarizada pode ser prejudicial, tendo em vista críticas baseadas neste ponto de vista antigo.

Introdução

Você conhece a linguagem Clojure? Costumo falar muito de linguagens como Common Lisp e Scheme, mas não cheguei a mostrar o que acho desta primeira. Algumas pessoas perguntam minha opinião a respeito da mesma, e inclusive me perguntam se realmente considero-a um dialeto de Lisp.

Pois bem, chegou a hora de dar uma dimensão do que penso a respeito de Clojure. Ressalto desde já que não gosto muito deste tipo de discussão, em especial porque detesto tratar de tópicos em que a maior parte da argumentação está atrelada a preferência pessoal. Mas não minimizo a importância do debate. Estou envolvido com a comunidade Common Lisp Brasil, e minhas ideias acabam perpassando a forma como enxergo CL, também. Todavia, que fique bem claro que esta é unicamente a minha opinião, e que não necessariamente reflete a opinião dos outros membros da comunidade Common Lisp Brasil.

A linguagem Clojure

Clojure é uma linguagem amplamente reconhecida como um dos muitos dialetos de Lisp. Sua plataforma primária é a Java Virtual Machine. Seu design foi feito por Rich Hickey, e já ouvi falar que este design foi feito de forma cuidadosa – algo crucial no desenvolvimento de uma linguagem de programação, e que nem sempre é levado em consideração de verdade.

Adicionalmente, Clojure é considerado um dialeto de Lisp pouco ortodoxo, o que de fato faz com que programadores de outros dialetos desconfiem das decisões tomadas em seu design – inclusive, não estou isento da realização destes questionamentos. De qualquer forma, é o dialeto de Lisp atualmente mais utilizado na indústria, tendo sobretudo seus ramos no Brasil, em empresas como a Nubank, por exemplo.

Experiência pessoal

Sou um programador versátil, e tive contato com várias linguagens diferentes ao longo da minha vida. Inevitavelmente, desenvolvi uma opinião forte a respeito das linguagens que eu achava boas, e das características que eu gostava nestas linguagens. Mas ao ter contato com outras linguagens e programadores que eram – ainda bem! – muito mais razoáveis que eu, aprendi que é necessário um grau de tolerância para uma opinião madura a respeito do assunto, juntamente com um toque de pé-no-chão. Em suma, eis algumas máximas interessantes que explicam parte da minha visão:

  • De nada adianta uma linguagem que resolve seu problema e não o faz de forma elegante;
  • De nada adianta uma linguagem elegante que não resolve seu problema.

Estas duas frases criam um balanço tênue entre pragmatismo e expressividade. Admito que parte disso é culpa das escolas de pensamento do SICP e da Filosofia UNIX, das quais gosto muito.

Quando falo em elegância, estou me referindo ao conceito de criação de peças simples que se conectam para uma funcionalidade mais complexa. Em outras palavras, refiro-me a conceber componentes pequenos, que interagem de forma transparente. Isto também pode ser compreendido através da ideia de robustez, mas o conceito de elegância não significa que todo e qualquer software feito em uma linguagem com esta característica seja, necessariamente, robusto.

Software demanda trabalho e análise cuidadosa e não se cria da noite para o dia, então boa parte da robustez de um software depende apenas do programador; a linguagem elegante apenas possibilita que o programador crie software robusto com maior facilidade.

O que quero dizer com este raciocínio é que o desenvolvimento do seu software deve levar em consideração tanto a utilidade quanto a manutenção; e quando falamos em manutenção, exigimos robustez no software analisado, para que esta atividade seja facilitada. No entanto, o que vejo por aí é que algumas soluções desenvolvidas no mercado (sobretudo para desenvolvimento web) esqueceram-se disso, e tornaram-se um arranjo de bugs e problemas escorrendo por todos os cantos. Mas este é um tópico para outro dia.

Clojure é um Lisp, afinal?

Um ponto interessante é o quanto o design de Clojure desvia-se de outros Lisps. Já vi pessoas chamarem Clojure de linguagem Lisp-like, sugerindo que esta seja uma linguagem que emula a sintaxe de um Lisp mas não o é. Acredito que esta opinião seja injusta. Para contra-argumentar, eu mantinha minha própria lista arbitrária de opiniões a respeito do que caracteriza uma linguagem como Lisp.

Todavia, percebi que esta lista arbitrária era uma péssima ferramenta de análise da linguagem, assim como tantas outras listas arbitrárias de características já boladas por outros internautas. Resolvi descartá-la, em prol de uma aproximação muito melhor que já exponho a seguir. Inclusive, esta discussão acerca de "o que é ou não Lisp" já causou brigas homéricas em listas de e-mails, drama que não pretendo escavar ou reproduzir através deste artigo. De qualquer forma, ficar classificando linguagens entre Lisp e não-Lisp não é algo tão valoroso, exceto em casos muito extremos1; mais valoroso ainda é determinar o quanto a linguagem retém do que se espera de um Lisp, como coloco a seguir.

Classificando Lisps

Mesmo considerando Clojure um Lisp, ainda me parece insatisfatória essa classificação discreta. Estou muito inclinado a aceitar este ponto de vista, mas algumas linguagens, que clamam ser dialetos de Lisp, são mais difíceis eu engolir. Um exemplo gritante do que considero um pseudo-Lisp é LFE (Lisp Flavored Erlang). Eu gosto de usar LFE, e seu uso melhorou a escrita de alguns programas meus, mas vejo LFE como nada mais que Erlang em pele de Lisp2.

Parece-me que existe uma escala, onde os "Lisps Raiz" e os "Falsos Lisps" colocam-se3. Mas o que exatamente esta escala mede?

O Eduardo Lopes, da Common Lisp Brasil, me deu uma ideia interessante quanto a isso: ao invés de nos perguntarmos "Linguagem X é um Lisp?", devemos nos perguntar "Linguagem X conserva o poder expressivo e a versatilidade originais dos Lisps?". Assim, poderíamos ter uma boa base de debate, e ao mesmo tempo abster-nos de discussões improdutivas.

Antes de aplicarmos este conceito, é interessante que eu explicite o que entendo como "poder expressivo" e "versatilidade" de Lisp, que não necessariamente seguem o mesmo entendimento do Eduardo. Expressividade, para mim, está diretamente associada a sintaxe; isto envolve ter sintaxe simples e não-ambígua, mas que ainda assim permita extensão irrestrita. E veja que este poder chega ao ponto em que, para expressões subsequentes que foram construídas por esta base expressiva, a simplicidade e não-ambiguidade das mesmas ficam à mercê do bom-senso do programador4.

A versatilidade estaria associada ao aspecto da homoiconicidade que, a meu ver, transita entre sintaxe e semântica: ainda que tenhamos listas como elementos sintáticos que permeiam a escrita, temos também as mesmas listas como estruturas manipuláveis, na semântica dos programas. Em outras palavras, Lisps são versáteis porque o que você declara sintaticamente é uma estrutura de dados, que é interpretada como tal, e pode ser manipulada, inclusive em runtime.

Agora sim podemos retornar à escala, e dizer que os "Lisps Raiz" são as linguagens que melhor conservam o poder expressivo e a versatilidade de LISP 1.5. Entre estas, alegremente coloco dialetos desaparecidos como ZetaLisp, Maclisp, Interlisp, Lisp Machine Lisp (das máquinas da Symbolics, por exemplo); também coloco Common Lisp e Emacs Lisp. Scheme e Racket ficariam um pouco mais longe de LISP 1.5, mas ainda estariam ali, firmes e fortes.

Clojure, então, eu veria como algo que transita para o meio da escala. Independentemente da conveniência do design da linguagem, esta ainda quebra muitas convenções de expressividade e versatilidade que foram pré-estabelecidas nos dialetos anteriores. Clojure não deriva de nenhum dialeto anterior de Lisp em particular; Common Lisp e Emacs Lisp tiveram dialetos muito inspirados em LISP 1.5 como precursores.

Sendo assim, Clojure tem uma característica forte de rompimento com toda uma cultura da família de linguagens. Isto não é algo necessariamente prejudicial, mas deixa claro um bom motivo para o gosto amargo que o dialeto poderia deixar na boca de programadores Lisp mais experientes.

Raiva comunitária

Como um último tópico, eu gostaria de analisar um pouco as discussões e respostas negativas que se colocam ao redor da linguagem Clojure, sobretudo considerando o que vi neste post. Vou tentar conciliar com críticas tanto aos programadores Lisp mais "tradicionais" quanto aos programadores Clojure que se manifestaram. Obviamente, não foram opiniões salutares, e não capturam a totalidade de nenhuma comunidade.

Utilizo o termo "tradicionais" com certo resguardo, pois o autor da postagem em questão também deixa claro que acha Common Lisp uma "abominação", e tenta comparar Clojure à experiência que o mesmo teve com máquinas da Symbolics. Pessoalmente, nunca utilizei uma Lisp Machine, mas ainda pretendo fazê-lo e escrever a respeito.

Deixo também claro que estes comentários datam de 2012, tendo completado sete anos no momento de escrita deste post. Opiniões podem ter mudado, e eu posso estar chutando cachorro morto e fazendo papel de advogado do diabo ao mesmo tempo.

O "lado" clojurista

"Posts like these are the reason I was never interested in being a part of the CL community. If you want this dream to come true, start playing the f****** game."

O autor deixa claro, como expliquei anteriormente, que seu objetivo não é fazer com que interessados em Lisp converjam para Common Lisp ao invés de Clojure. Ao invés disso, ele tenta apontar, ainda que não seja de forma adequada, que existe um legado que Clojure parece não respeitar, e por isso ele não acha Clojure um bom Lisp.

"…make Common Lisp libraries that solve real problems…"

Atualmente, há bibliotecas em Common Lisp para resolver uma gama de problemas ditos "reais"5. A comunidade japonesa de Common Lisp, em particular, está muito ativa atualmente, e cria ferramentas muito boas, desde servidores web de baixa latência a formas de distribuição e instalação de software escrito em Common Lisp (veja Roswell).

"…most of its libraries are undocumented, half-implemented hacks…"

Este é definitivamente um dos problemas mais sérios da comunidade de Common Lisp. Muitas das ferramentas pecam enormemente em documentação (o autor tenta negar isto em seus comentários). Este problema e seus possíveis motivos foram abordados por Rudolf Winestock em seu famoso artigo The Lisp Curse, que deixo como leitura adicional ao invés de tentar repetir o que está exposto nele.

"I can't build usable software in CL anywhere near as short a time as I can with Java"

Tomando muito cuidado aqui, vou dizer que, se esta frase descreveu uma experiência genuína, então havia algum problema com a forma de desenvolvimento da pessoa que a escreveu. Tive a experiência de desenvolver alguns projetos em Common Lisp, incluindo um pequeno compilador. Este pequeno compilador possuía um modelo para que fosse desenvolvido em Java passo-a-passo.

Ao invés de utilizar-me do modelo exposto no material didático que segui, arquitetei o software da forma como eu quis e, com uma boa dose de organização, pude perceber que os ciclos de teste eram extremamente mais rápidos que qualquer aplicação que eu já tivesse feito com Java ou C++; o mero fato de ter um REPL à disposição e reinterpretação seletiva de definições já tornou muito fácil e rápida a depuração de problemas e os testes locais.

Este é um argumento anedótico, é claro, mas satisfatório como resposta para outro argumento anedótico. Ainda assim, caro leitor, é mais interessante que você mesmo analise as potencialidades de Java e Common Lisp e tire suas próprias conclusões, ao invés de acatar as minhas.

O "lado" tradicional

"Common Lisp is a Stradivari violin, not a kazoo. It simply isn't for you…"

Considero esta uma resposta muito ruim, e também é o tipo de pensamento que fazemos questão de não cultivar na Common Lisp Brasil. Todo e qualquer programador com interesse em Common Lisp deveria ser bem-vindo à comunidade. Há pessoas na comunidade brasileira que simpatizam e que não simpatizam com Clojure, algumas até mesmo utilizam a linguagem no trabalho.

Se alguém considera Common Lisp melhor que Clojure, o ideal é mostrar a opinião de maneira respeitosa e intelectualmente honesta.

"F*** your so-called 'facts of life'. Superior mind-amplifying tools existed and will exist again."

Falar de Lisps antigos (e também de Common/Emacs Lisp) como ferramentas superiores e amplificadoras de mentes soa pedante, mas por trás desta agressividade (totalmente desnecessária, diga-se de passagem), podemos destacar o ponto do autor: sua maior crítica é dizer que Clojure é uma linguagem que encoraja o programador a não importar-se com a forma como as coisas funcionam por baixo.

Esta ideia parece contra-intuitiva do ponto de vista de uma linguagem de alto-nível, mas o que o autor explica é que mesmo Lisps como Common Lisp possuem um senso de "construção" na constituição de seus elementos, uma ideia bem palpável do que possibilita a abstração que você está escrevendo na tela; e para ele, em Clojure, você apenas escreve o que ele chama de "magia negra" e se dá por vencido. Para todos os efeitos, aqui entram as ideias de robustez, simplicidade e versatilidade, construídas por uma linguagem suficientemente expressiva. Estes aspectos foram discutidos anteriormente.

É importante lembrar que esta característica das ferramentas de desenvolvimento em particular – independentemente de ser algo realmente observável em Clojure ou não – é o curso do desenvolvimento de software na indústria, que já perdura por décadas, não sendo um problema apenas da comunidade Lisp. Infelizmente, alguns desenvolvedores (e muitas empresas!) não se importam com robustez em seu software, e um famigerado Programador Rick (ou um programador incompetente, mesmo) que vier resolver um problema grave de infraestrutura e performance, passando por cima de boas práticas, será coroado como heroi.

"…the Clojure community still replies to the criticisms therein with… only insults. This is what comes of a product fundamentally brain-damaged at birth."

Não há algo para ser escavado nesta mensagem, que está logo no topo do texto, mas eu gostaria de apontar apenas que esta é uma alfinetada proveniente do autor, que consequentemente só gera mais comentários raivosos de entusiastas de Clojure. Interessante notar que aqui perdura a máxima "não existe marketing negativo", porque este ataque extra só atrai mais acessos, e que uma postagem ou comentário raivosos tendem a não atrair uma discussão sensata; este curso de ação pode ser observado através dos comentários ali deixados.

"The cult of Good Enough which seems to pervade all of modern computing has finally chewed its way through to the Lisp community"

Esta passagem possuía, originalmente, um link para o texto The Rise of "Worse is Better", de Richard Gabriel. Esta filosofia curiosa busca tomar a Filosofia UNIX e dobrá-la em um outro eixo, colocando a simplicidade de interface e implementação com maior peso que outros atributos da Filosofia UNIX, como consistência e corretude.

Não pretendo entrar no mérito da validade desta ideia, mas gostaria de discutir a expressão "Cult of Good Enough". A meu ver, ela tem duas conotações: a primeira, mais óbvia e pretendida pelo autor, diz respeito ao software feito "nas coxas" (vide meu comentário para a citação sobre "facts of life").

Mas, nesta expressão, há também outra conotação: a ideia do comodismo, que é onde o argumento do autor do texto sai pela culatra, e que pode contaminar a impressão que um programador de outros dialetos de Lisp tem sobre Clojure (veja, isto também inclui a mim). O que parece bom o suficiente aos nossos olhos tende a sugerir estagnação, a ponto de tornar-se desgosto por novas sugestões. Até onde estamos disponíveis para perceber coisas novas?

Conclusão

Este último pensamento coloca na mesa o local onde eu gostaria de chegar, e também soma-se ao que falei anteriormente para mostrar a totalidade do meu ponto de vista.

Lisps são linguagens maleáveis por definição. Talvez isto seja proveniente da cultura, talvez seja proveniente da homoiconicidade, ou de qualquer aspecto mostrado antes, mas esta maleabilidade é melhor percebida na forma como as ferramentas são construídas ao utilizar a linguagem. O programador Lisp experiente é capaz de entrar em um estado de fluxo muito rapidamente, uma vez que a expressão sintática fica de lado e dá espaço à semântica dos programas.

Todavia, esta maleabilidade também demanda um bom nível de receptividade ao novo, porque ela vem normalmente acompanhada de um processo criativo. LISP 1.5 foi originalmente criada para lidar com inteligência artificial, e McCarthy acreditava que Lisp era uma forma superior de realizar computação, por ser sintaticamente simples6. Mas uma família de linguagens tão poderosa não necessariamente faz com que seus programadores tenham uma mente maleável com relação a novas ideias.

Ressalto também que uma mente aberta não pressupõe esquecer suas origens. Criar algo novo envolve obter conhecimento do que veio antes, para percebermos as coisas boas que foram feitas e que podem ser reaproveitadas. O paradigma funcional em geral, por exemplo, não é algo novo, mas foi redescoberto quando percebeu-se que software predominantemente feito de forma funcional escala muito bem, quando colocado em conjunto com programação concorrente, clusters ou até microsserviços.

Cabe aos seres sensatos das comunidades de programação o ato de inventar sem reinventar: Tomar o que há de bom, e mudar o que verdadeiramente não está bom. Assim, talvez finalmente percebamos que não existe bala de prata em desenvolvimento de software, e que sempre haverá espaço para experimentação com boas ideias, sejam elas novas ou não-tão-novas-assim.

Notas de Rodapé:

1

O importante aqui é lembrar que existem linguagens que simplesmente não são Lisps. JavaScript é uma dessas linguagens, e é o motivo de eu ter adicionado o link referenciado.

2

Se não me engano, este é exatamente o propósito de LFE: não ser um Lisp de fato, mas promover uma melhor escrita de programas em Erlang através de uma sintaxe similar a Lisp.

3

Linguagens como JavaScript não estão convidadas para a escala, sinto muito. Aqui só entram linguagens que poderiam se passar por Lisps; e deixo aqui esta definição vaga de forma proposital, porque percepção é subjetiva, e portanto cabe debate.

4

Saliento que este conceito de simplicidade é um apelo ao fluxo sintático, e não necessariamente à forma como a linguagem foi projetada. Em outras palavras, se é possível ser extremamente expressivo com pouca e inteligível escrita e, se isto não for possível, se há liberdade para que o programador crie a própria forma de expressão sintática.

5

Confesso que já ouvi o termo "problemas reais" mais de uma vez, e ainda não o compreendo; se há um problema que demanda uma solução tecnológica, ele só pode ser real. Um problema que não seja real me parece indicativo de necessidade de consulta psiquiátrica. Normalmente este termo é jogado ao vento por gerentes de projeto que se entitulam muito pragmáticos, para designar os problemas que não pertencem ao escopo do software desenvolvido pela empresa onde trabalham.

6

"McCarthy wanted to show that Lisp was a superior way to describe computation; he thought that the best way to do that was to create the 'universal Lisp function'". Em Lisp and the foundations of computing, parágrafo 16.

De volta à página anterior