CONCISÃO É PODER
OriginalMay 2002
"A quantidade de significado comprimido em um pequeno espaço por sinais algébricos, é outra circunstância que facilita os raciocínios que estamos acostumados a realizar com sua ajuda."
- Charles Babbage, citado na palestra de Iverson para o Prêmio Turing
Na discussão sobre as questões levantadas por Revenge of the Nerds na lista de discussão LL1, Paul Prescod escreveu algo que ficou na minha mente.
O objetivo do Python é regularidade e legibilidade, não concisão.
À primeira vista, isso parece uma afirmação bastante condenatória sobre uma linguagem de programação. Pelo que sei, concisão = poder. Se assim for, então, substituindo, obtemos
O objetivo do Python é regularidade e legibilidade, não poder.
e isso não parece um compromisso (se é um compromisso) que você gostaria de fazer. Não está muito longe de dizer que o objetivo do Python não é ser eficaz como uma linguagem de programação.
Concisão = poder? Isso me parece uma pergunta importante, talvez a pergunta mais importante para qualquer pessoa interessada em projeto de linguagem, e uma que seria útil enfrentar diretamente. Não tenho certeza ainda se a resposta é um simples sim, mas parece uma boa hipótese para começar.
Hipótese
Minha hipótese é que concisão é poder, ou é suficientemente próxima que, exceto em exemplos patológicos, você pode tratá-los como idênticos.
Parece-me que a concisão é para o que as linguagens de programação são destinadas. Os computadores ficariam tão felizes em ser informados sobre o que fazer diretamente em linguagem de máquina. Acho que o principal motivo pelo qual nos damos ao trabalho de desenvolver linguagens de alto nível é para obter alavancagem, para que possamos dizer (e mais importante, pensar) em 10 linhas de uma linguagem de alto nível o que exigiria 1000 linhas de linguagem de máquina. Em outras palavras, o principal ponto das linguagens de alto nível é tornar o código-fonte menor.
Se o código-fonte menor é o objetivo das linguagens de alto nível, e o poder de algo é o quão bem ele atinge seu objetivo, então a medida do poder de uma linguagem de programação é o quão pequena ela torna seus programas.
Inversamente, uma linguagem que não torna seus programas pequenos está fazendo um mau trabalho do que as linguagens de programação devem fazer, como uma faca que não corta bem, ou uma impressão ilegível.
Métricas
Pequeno em que sentido, então? A medida mais comum do tamanho do código é linhas de código. Mas acho que essa métrica é a mais comum porque é a mais fácil de medir. Não acho que ninguém realmente acredite que seja o verdadeiro teste do comprimento de um programa. Diferente linguagens têm convenções diferentes sobre quanto você deve colocar em uma linha; em C, muitas linhas não têm nada além de um delimitador ou dois.
Outro teste fácil é o número de caracteres em um programa, mas isso também não é muito bom; algumas linguagens (Perl, por exemplo) apenas usam identificadores mais curtos do que outras.
Acho que uma medida melhor do tamanho de um programa seria o número de elementos, onde um elemento é qualquer coisa que seria um nó distinto se você desenhasse uma árvore representando o código-fonte. O nome de uma variável ou função é um elemento; um inteiro ou um número de ponto flutuante é um elemento; um segmento de texto literal é um elemento; um elemento de um padrão, ou uma diretiva de formato, é um elemento; um novo bloco é um elemento. Existem casos limítrofes (é -5 dois elementos ou um?) mas acho que a maioria deles é a mesma para todas as linguagens, então eles não afetam muito as comparações.
Essa métrica precisa ser desenvolvida e pode exigir interpretação no caso de linguagens específicas, mas acho que tenta medir a coisa certa, que é o número de partes que um programa tem. Acho que a árvore que você desenharia nesse exercício é o que você tem que fazer na sua cabeça para conceber o programa, e assim seu tamanho é proporcional ao trabalho que você tem que fazer para escrever ou ler.
Projeto
Esse tipo de métrica permitiria que comparássemos diferentes linguagens, mas isso não é, pelo menos para mim, seu principal valor. O principal valor do teste de concisão é como um guia no projeto de linguagens. A comparação mais útil entre linguagens é entre duas variantes potenciais da mesma linguagem. O que posso fazer na linguagem para tornar os programas mais curtos?
Se a carga conceitual de um programa é proporcional à sua complexidade, e um determinado programador pode tolerar uma carga conceitual fixa, então isso é o mesmo que perguntar, o que posso fazer para permitir que os programadores façam o máximo? E isso me parece idêntico a perguntar, como posso projetar uma boa linguagem?
(Aliás, nada torna mais evidente que o velho chavão "todas as linguagens são equivalentes" é falso do que projetar linguagens. Quando você está projetando uma nova linguagem, você está constantemente comparando duas linguagens - a linguagem se eu fizesse x, e se eu não fizesse - para decidir qual é melhor. Se essa fosse realmente uma pergunta sem sentido, você poderia muito bem jogar uma moeda.)
Visar a concisão parece uma boa maneira de encontrar novas ideias. Se você pode fazer algo que torna muitos programas diferentes mais curtos, provavelmente não é uma coincidência: você tem provavelmente descoberto uma nova abstração útil. Você pode até ser capaz de escrever um programa para ajudar pesquisando código-fonte para padrões repetidos. Entre outras linguagens, aquelas com reputação de concisão seriam as que deveriam ser procuradas por novas ideias: Forth, Joy, Icon.
Comparação
A primeira pessoa a escrever sobre essas questões, que eu saiba, foi Fred Brooks no Mês do Homem Mítico. Ele escreveu que os programadores pareciam gerar aproximadamente a mesma quantidade de código por dia, independentemente da linguagem. Quando li isso pela primeira vez na minha juventude, foi uma grande surpresa para mim e parecia ter implicações enormes. Significava que (a) a única maneira de fazer o software ser escrito mais rápido era usar uma linguagem mais concisa, e (b) alguém que se desse ao trabalho de fazer isso poderia deixar os concorrentes que não o fizessem na poeira.
A hipótese de Brooks, se for verdadeira, parece estar no coração da pirataria. Nos anos que se seguiram, prestei muita atenção a qualquer evidência que pudesse obter sobre a questão, desde estudos formais até anedotas sobre indivíduos projetos. Não vi nada que o contradissesse.
Ainda não vi evidências que me parecessem conclusivas, e não espero ver. Estudos como a comparação de linguagens de programação de Lutz Prechelt, enquanto gerando o tipo de resultados que eu esperava, tendem a usar problemas que são muito curtos para serem testes significativos. Um teste melhor de uma linguagem é o que acontece em programas que levam um mês para serem escritos. E o único teste real, se você acredita como eu que o principal objetivo de uma linguagem é ser boa para pensar (em vez de apenas dizer a um computador o que fazer uma vez que você pensou nisso) é quais coisas novas você pode escrever nela. Então, qualquer comparação de linguagem onde você tem que atender a uma especificação predefinida está testando um pouco a coisa errada.
O verdadeiro teste de uma linguagem é o quão bem você pode descobrir e resolver novos problemas, não o quão bem você pode usá-la para resolver um problema que outra pessoa já tem formulado. Esses dois são critérios bem diferentes. Na arte, meios como bordado e mosaico funcionam bem se você souber de antemão o que quer fazer, mas são absolutamente ruins se você não sabe. Quando você quer descobrir a imagem enquanto a faz - como você tem que fazer com qualquer coisa tão complexa quanto uma imagem de um pessoa, por exemplo - você precisa usar um meio mais fluido como lápis ou lavagem de tinta ou óleo. E de fato, a maneira como tapeçarias e mosaicos são feitos em a prática é fazer uma pintura primeiro, depois copiá-la. (A palavra "desenho" foi originalmente usada para descrever uma pintura destinada a esse propósito).
O que isso significa é que provavelmente nunca teremos comparações precisas do poder relativo das linguagens de programação. Teremos comparações precisas, mas não precisas. Em particular, estudos explícitos com o propósito de comparar linguagens, porque provavelmente usarão problemas pequenos e necessariamente usarão problemas predefinidos, tenderão a subestimar o poder do linguagens mais poderosas.
Relatórios de campo, embora necessariamente menos precisos do que estudos "científicos", provavelmente serão mais significativos. Por exemplo, Ulf Wiger da Ericsson fez um estudo que concluiu que Erlang era 4-10x mais conciso que C++, e proporcionalmente mais rápido para desenvolver software em:
Comparações entre projetos de desenvolvimento internos da Ericsson indicam produtividade semelhante de linha/hora, incluindo todas as fases de desenvolvimento de software, independentemente de qual linguagem (Erlang, PLEX, C, C++ ou Java) foi usado. O que diferencia as diferentes linguagens então se torna o código-fonte volume.
O estudo também trata explicitamente de um ponto que foi apenas implícito no livro de Brooks (já que ele mediu linhas de código depurado): programas escritos em linguagens mais poderosas tendem a ter menos bugs. Isso se torna um fim em si mesmo, possivelmente mais importante do que o programador produtividade, em aplicações como switches de rede.
O Teste de Sabor
Em última análise, acho que você tem que ir com o seu intestino. Como é a sensação de programar na linguagem? Acho que a maneira de encontrar (ou projetar) a melhor linguagem é se tornar hipersensível ao quão bem uma linguagem permite que você pense, então escolha/projete a linguagem que se sentir melhor. Se algum recurso de linguagem for desajeitado ou restritivo, não se preocupe, você saberá disso.
Tal hipersensibilidade terá um custo. Você descobrirá que não pode suportar programar em linguagens desajeitadas. Acho incrivelmente restritivo programar em linguagens sem macros, assim como alguém que usou digitação dinâmica acha incrivelmente restritivo ter que voltar para programar em uma linguagem onde você tem que declarar o tipo de cada variável e não pode fazer uma lista de objetos de tipos diferentes.
Não sou o único. Conheço muitos hackers Lisp que isso aconteceu com. Na verdade, a medida mais precisa do poder relativo da programação linguagens podem ser a porcentagem de pessoas que conhecem a linguagem que aceitará qualquer trabalho onde possa usar essa linguagem, independentemente do domínio da aplicação.
Restrição
Acho que a maioria dos hackers sabe o que significa uma linguagem se sentir restritiva. O que está acontecendo quando você sente isso? Acho que é a mesma sensação que você tem quando a rua que você quer pegar está bloqueada e você tem que fazer um longo desvio para chegar aonde queria ir. Há algo que você quer dizer, e a linguagem não permite.
O que realmente está acontecendo aqui, acho, é que uma linguagem restritiva é uma que não é suficientemente concisa. O problema não é simplesmente que você não pode dizer o que você planejou. É que o desvio que a linguagem faz você fazer é mais longo. Tente este experimento mental. Suponha que houvesse algum programa que você queria escrever, e a linguagem não permitisse que você o expressasse da maneira que você planejou, mas em vez disso o obrigasse a escrever o programa de alguma outra forma que fosse mais curta. Para mim, pelo menos, isso não pareceria muito restritivo. Seria como a rua que você queria pegar sendo bloqueada, e o policial na interseção te direcionando para um atalho em vez de um desvio. Ótimo!
Acho que a maioria (noventa por cento?) do sentimento de restrição vem de ser forçado a fazer o programa que você escreve na linguagem mais longa do que um que você tem na sua cabeça. Restrição é principalmente falta de concisão. Então, quando uma linguagem parece restritiva, o que isso (principalmente) significa é que ela não é suficientemente concisa, e quando uma linguagem não é concisa, ela se sentirá restritiva.
Legibilidade
A citação com a qual comecei menciona outras duas qualidades, regularidade e legibilidade. Não tenho certeza do que é regularidade, ou qual vantagem, se houver, o código que é regular e legível tem sobre o código que é simplesmente legível. Mas acho que sei o que se entende por legibilidade, e acho que também está relacionado à concisão.
Temos que ter cuidado aqui para distinguir entre a legibilidade de uma linha individual de código e a legibilidade de todo o programa. É o segundo que importa. Concordo que uma linha de Basic provavelmente será mais legível do que uma linha de Lisp. Mas um programa escrito em Basic é vai ter mais linhas do que o mesmo programa escrito em Lisp (especialmente quando você cruza para Greenspunland). O esforço total para ler o programa Basic certamente será maior.
esforço total = esforço por linha x número de linhas
Não tenho tanta certeza de que a legibilidade é diretamente proporcional à concisão como eu sou de que o poder é, mas certamente a concisão é um fator (no sentido matemático; veja a equação acima) na legibilidade. Então, pode nem ser significativo dizer que o objetivo de uma linguagem é legibilidade, não concisão; poderia ser como dizer que o objetivo era legibilidade, não legibilidade.
O que a legibilidade por linha significa, para o usuário que encontra a linguagem pela primeira vez, é que o código-fonte parecerá não ameaçador. Então legibilidade por linha poderia ser uma boa decisão de marketing, mesmo que seja uma má decisão de design. É isomórfico à técnica muito bem-sucedida de deixar as pessoas pagarem em parcelas: em vez de assustá-las com um alto preço inicial, você informa a elas o baixo pagamento mensal. Planos de pagamento são uma perda líquida para o comprador, embora a mera legibilidade por linha provavelmente seja para o programador. O comprador vai fazer muitos desses pagamentos baixos, baixos; e o programador vai ler muitas dessas linhas individualmente legíveis.
Esse compromisso é anterior às linguagens de programação. Se você está acostumado a ler romances e artigos de jornal, sua primeira experiência lendo um artigo de matemática pode ser decepcionante. Pode levar meia hora para ler uma única página. E ainda, tenho certeza de que a notação não é o problema, embora possa parecer que é. O artigo de matemática é difícil de ler porque as ideias são difíceis. Se você expressasse as mesmas ideias em prosa (como os matemáticos tinham que fazer antes de desenvolverem notações concisas), elas não seriam mais fáceis de ler, porque o artigo cresceria para o tamanho de um livro.
Até que ponto?
Várias pessoas rejeitaram a ideia de que concisão = poder. Acho que seria mais útil, em vez de simplesmente argumentar que eles são iguais ou não, perguntar: até que ponto a concisão = poder? Porque claramente a concisão é uma grande parte do que as linguagens de alto nível são para. Se não é tudo para o que elas são para, então para que mais elas são, e quão importantes, relativamente, são essas outras funções?
Não estou propondo isso apenas para tornar o debate mais civilizado. Eu realmente quero saber a resposta. Quando, se alguma vez, uma linguagem é muito concisa para o seu próprio bem?
A hipótese com a qual comecei foi que, exceto em exemplos patológicos, achei que a concisão poderia ser considerada idêntica ao poder. O que eu quis dizer foi que em qualquer linguagem que alguém projetasse, elas seriam idênticas, mas que se alguém quisesse projetar uma linguagem explicitamente para refutar essa hipótese, provavelmente conseguiria. Não estou nem mesmo certo disso, na verdade.
Linguagens, não programas
Devemos deixar claro que estamos falando sobre a concisão de linguagens, não de programas individuais. Certamente é possível que programas individuais sejam escritos com muita densidade.
Escrevi sobre isso em On Lisp. Uma macro complexa pode ter que salvar muitas vezes seu próprio comprimento para ser justificada. Se escrever alguma macro cabeluda pudesse economizar dez linhas de código toda vez que você a usasse, e a macro em si tem dez linhas de código, então você obtém uma economia líquida em linhas se você a usar mais de uma vez. Mas isso ainda poderia ser uma má jogada, porque as definições de macro são mais difíceis de ler do que o código comum. Você pode ter que usar a macro dez ou vinte vezes antes que ela gere uma rede melhoria na legibilidade.
Tenho certeza de que todas as linguagens têm esses compromissos (embora eu suspeite que as apostas aumentem à medida que a linguagem se torna mais poderosa). Todo programador deve ter visto código que alguma pessoa esperta tornou marginalmente mais curto usando truques de programação duvidosos.
Então, não há discussão sobre isso - pelo menos, não da minha parte. Individual programas certamente podem ser muito concisos para o seu próprio bem. A questão é, uma linguagem pode ser? Uma linguagem pode obrigar os programadores a escrever código que é curto (em elementos) à custa da legibilidade geral?
Uma razão pela qual é difícil imaginar uma linguagem sendo muito concisa é que se houvesse alguma maneira excessivamente compacta de expressar algo, provavelmente haveria também uma maneira mais longa. Por exemplo, se você sentisse que os programas Lisp usando muitas macros ou funções de ordem superior eram muito densos, você poderia, se você preferisse, escrever código que fosse isomórfico ao Pascal. Se você não quiser expressar fatorial em Arc como uma chamada para uma função de ordem superior
(rec zero 1 * 1-)
você também pode escrever uma definição recursiva:
(rfn fact (x) (if (zero x) 1 (* x (fact (1- x)))))
Embora não consiga pensar em nenhum exemplo de cabeça, estou interessado na questão de saber se uma linguagem pode ser muito concisa. Existem linguagens que o forçam a escrever código de uma forma que é complicada e incompreensível? Se alguém tiver exemplos, ficaria muito interessado em vê-los.
(Lembrete: O que estou procurando são programas que são muito densos de acordo com a métrica de "elementos" esboçada acima, não apenas programas que são curtos porque os delimitadores podem ser omitidos e tudo tem um nome de um caractere.)