CONCISÃO É PODER
OriginalMaio de 2002
"A quantidade de significado comprimida 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 no Discurso de Premiação de Turing de Iverson
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 em minha mente.
O objetivo do Python é a regularidade e a legibilidade, não a concisão.
À primeira vista, isso parece uma coisa bastante condenatória a se afirmar sobre uma linguagem de programação. Na minha opinião, concisão = poder. Se for assim, então substituindo, obtemos
O objetivo do Python é a regularidade e a legibilidade, não o poder.
e isso não parece um trade-off (se é que é um trade-off) 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.
A concisão = poder? Isso me parece uma questão importante, talvez a questão mais importante para quem se interessa por design de linguagens, e uma que seria útil enfrentar diretamente. Ainda não tenho certeza de que a resposta seja simplesmente sim, mas parece uma boa hipótese para começar.
Hipótese
Minha hipótese é que a concisão é poder, ou está próxima o suficiente para que, exceto em exemplos patológicos, você possa tratá-los como idênticos.
Parece-me que a concisão é o que as linguagens de programação são para. Os computadores ficariam igualmente felizes em receber instruções diretamente em linguagem de máquina. Acho que a principal razão pela qual nos damos o trabalho de desenvolver linguagens de alto nível é para obter alavancagem, de modo 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 objetivo das linguagens de alto nível é tornar o código-fonte menor.
Se o código-fonte menor é o propósito das linguagens de alto nível, e o poder de algo é o quão bem ele atinge seu propósito, então a medida do poder de uma linguagem de programação é o quão pequeno ela torna seus programas.
Por outro lado, uma linguagem que não torna seus programas menores está fazendo um mau trabalho do que as linguagens de programação supostamente devem fazer, como uma faca que não corta bem ou uma impressão ilegível.
Métricas
Mas pequeno em que sentido? A medida mais comum do tamanho do código é o número de linhas de código. Mas acho que essa métrica é a mais comum porque é a mais fácil de medir. Não acho que alguém realmente acredite que é o verdadeiro teste do comprimento de um programa. Diferentes linguagens têm diferentes convenções 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 esse também não é muito bom; algumas linguagens (Perl, por exemplo) simplesmente usam identificadores mais curtos do que outras.
Acho que uma melhor medida 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 detalhada, e pode exigir interpretação no caso de linguagens específicas, mas acho que ela 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 em sua cabeça para conceber o programa, e então seu tamanho é proporcional à quantidade de trabalho que você tem que fazer para escrevê-lo ou lê-lo.
Design
Esse tipo de métrica nos permitiria comparar diferentes linguagens, mas não é, pelo menos para mim, seu principal valor. O principal valor do teste de concisão é como um guia no design 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 deixa mais evidente que o velho ditado "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 realmente fosse uma questão sem sentido, você poderia simplesmente jogar uma moeda.)
Visar a concisão parece uma boa maneira de encontrar novas ideias. Se você puder fazer algo que torne muitos programas diferentes mais curtos, provavelmente não é uma coincidência: você provavelmente descobriu uma nova abstração útil. Você até pode escrever um programa para ajudar, procurando código-fonte por padrões repetidos. Entre outras linguagens, aquelas com reputação de concisão seriam as que você deveria olhar para novas ideias: Forth, Joy, Icon.
Comparação
A primeira pessoa a escrever sobre essas questões, que eu saiba, foi Fred Brooks em The Mythical Man Month. 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, ainda na minha casa dos vinte anos, foi uma grande surpresa para mim e pareceu ter enormes implicações. Isso significava que (a) a única maneira de escrever software mais rápido era usar uma linguagem mais concisa, e (b) alguém que se desse o trabalho de fazer isso poderia deixar os concorrentes que não fizeram isso para trás.
A hipótese de Brooks, se for verdadeira, parece estar no cerne do hacking. Nos anos desde então, tenho prestado muita atenção a qualquer evidência que eu pudesse obter sobre a questão, de estudos formais a anedotas sobre projetos individuais. 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, embora gerando o tipo de resultados que eu esperava, tendem a usar problemas que são muito curtos para serem testes significativos. Um melhor teste de uma linguagem é o que acontece em programas que levam um mês para escrever. E o único teste real, se você acreditar como eu que o principal propósito de uma linguagem é ser boa para pensar (em vez de apenas dizer a um computador o que fazer depois que você pensou nisso) é o que você pode escrever de novo nela. Então qualquer comparação de linguagem em que você tem que atender a uma especificação predefinida está testando ligeiramente 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 alguém já formulou. Esses dois são critérios bastante 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 souber. Quando você quer descobrir a imagem à medida que a faz-- como você tem que fazer com qualquer coisa tão complexa quanto uma imagem de uma 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 as tapeçarias e mosaicos são feitos na prática é fazer uma pintura primeiro, depois copiá-la. (A palavra "cartoon" foi originalmente usada para descrever uma pintura destinada a esse fim).
O que isso significa é que 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 das linguagens mais poderosas.
Relatórios do 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 o Erlang era 4-10x mais conciso que o C++ e proporcionalmente mais rápido para desenvolver software:
[1]
Comparações entre projetos de desenvolvimento internos da Ericsson indicam produtividade de linha/hora semelhante, incluindo todas as fases do desenvolvimento de software, independentemente de qual linguagem (Erlang, PLEX, C, C++ ou Java) foi usada. O que diferencia as diferentes linguagens então se torna o volume do código-fonte.
O estudo também lida explicitamente com um ponto que estava apenas implícito no livro de Brooks (uma vez 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 a produtividade do programador, em aplicativos como switches de rede.
O Teste de Gosto
No final, acho que você tem que confiar no seu instinto. Como é programar na linguagem? Acho que a maneira de encontrar (ou projetar) a melhor linguagem é se tornar hipersensível a quão bem uma linguagem permite que você pense, então escolha/projete a linguagem que se sente melhor. Se algum recurso da linguagem for desajeitado ou restritivo, não se preocupe, você saberá sobre isso.
Tal hipersensibilidade terá um custo. Você descobrirá que não consegue suportar programar em linguagens desajeitadas. Acho insuportavelmente restritivo programar em linguagens sem macros, assim como alguém acostumado à digitação dinâmica acha insuportavelmente restritivo ter que voltar a programar em uma linguagem onde você precisa 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 de Lisp a quem isso aconteceu. Na verdade, a medida mais precisa do poder relativo das linguagens de programação pode ser o percentual de pessoas que conhecem a linguagem que aceitarão qualquer emprego onde possam usar essa linguagem, independentemente do domínio da aplicação.
Restritividade
Acho que a maioria dos hackers sabe o que significa para 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ê precisa dar uma longa volta para chegar onde queria ir. Há algo que você quer dizer e a linguagem não deixa.
O que realmente está acontecendo aqui, acho eu, é que uma linguagem restritiva é uma que não é sucinta o suficiente. O problema não é simplesmente que você não pode dizer o que planejava. É que o desvio que a linguagem faz você tomar é mais longo. Tente este experimento mental. Suponha que houvesse algum programa que você quisesse escrever e a linguagem não deixasse você expressá-lo da maneira que você planejava, mas em vez disso o forçasse a escrever o programa de outra maneira que fosse mais curta. Para mim, pelo menos, isso não pareceria muito restritivo. Seria como a rua que você queria pegar estar bloqueada e o policial na interseção o direcionar para um atalho em vez de um desvio. Ótimo!
Acho que a maior parte (noventa por cento?) da sensação de restritividade vem de ser forçado a fazer o programa que você escreve na linguagem ser mais longo do que aquele que você tem em sua cabeça. A restritividade é principalmente falta de concisão. Então, quando uma linguagem parece restritiva, o que isso (principalmente) significa é que ela não é sucinta o suficiente e, quando uma linguagem não é sucinta, ela parecerá restritiva.
Legibilidade
A citação com a qual comecei menciona outras duas qualidades, regularidade e legibilidade. Não tenho certeza do que é regularidade ou que vantagem, se houver, o código que é regular e legível tem sobre o código que é apenas 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 de código individual e a legibilidade de todo o programa. É a segunda 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 terá mais linhas do que o mesmo programa escrito em Lisp (especialmente quando você cruza para o território de Greenspun). O esforço total de leitura do 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 seja diretamente proporcional à concisão como tenho de que o poder seja, 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 é a legibilidade, não a concisão; poderia ser como dizer que o objetivo era a legibilidade, não a legibilidade.
O que a legibilidade por linha significa, para o usuário que está encontrando a linguagem pela primeira vez, é que o código-fonte parecerá inofensivo. Portanto, a legibilidade por linha pode 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 preço inicial alto, você lhes diz o baixo pagamento mensal. Os planos de pagamento parcelado são um prejuízo líquido para o comprador, embora a mera legibilidade por linha provavelmente seja para o programador. O comprador vai fazer muitos desses pagamentos baixos e baixos; e o programador vai ler muitas dessas linhas individualmente legíveis.
Essa compensação precede as linguagens de programação. Se você está acostumado a ler romances e artigos de jornal, sua primeira experiência de leitura de um artigo matemático pode ser desanimadora. Pode levar meia hora para ler uma única página. E, no entanto, tenho certeza de que a notação não é o problema, mesmo que possa parecer que é. O artigo matemático é difícil de ler porque as ideias são difíceis. Se você expressasse as mesmas ideias em prosa (como os matemáticos tiveram que fazer antes de evoluírem notações sucintas), elas não seriam mais fáceis de ler, porque o artigo cresceria até o tamanho de um livro.
Até que ponto?
Várias pessoas rejeitaram a ideia de que a concisão = poder. Acho que seria mais útil, em vez de simplesmente argumentar que eles são os mesmos 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 o que elas são para, então o que mais elas são para, 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 era de que, exceto em exemplos patológicos, eu achava que a concisão poderia ser considerada idêntica ao poder. O que eu queria dizer era que em qualquer linguagem que alguém projetaria, eles seriam idênticos, mas que se alguém quisesse projetar uma linguagem explicitamente para refutar essa hipótese, eles provavelmente poderiam fazê-lo. Nem mesmo tenho certeza disso, na verdade.
Linguagens, não Programas
Devemos deixar claro que estamos falando sobre a concisão das linguagens, não de programas individuais. Certamente é possível que programas individuais sejam escritos de forma muito densa.
Escrevi sobre isso em On Lisp. Uma macro complexa pode ter que economizar muitas vezes o seu próprio tamanho para ser justificada. Se escrever alguma macro complicada pudesse economizar dez linhas de código toda vez que você a usa, e a macro tem dez linhas de código em si, então você obtém uma economia líquida em linhas se a usar mais de uma vez. Mas isso ainda poderia ser uma má jogada, porque as definições de macros são mais difíceis de ler do que o código normal. Você pode ter que usar a macro dez ou vinte vezes antes que ela produza uma melhoria líquida na legibilidade.
Tenho certeza de que todas as linguagens têm tais compensações (embora eu suspeite que as apostas fiquem mais altas à 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. Programas individuais certamente podem ser muito sucintos para o seu próprio bem. A questão é: uma linguagem pode ser? Uma linguagem pode forçar os programadores a escrever código que seja curto (em elementos) às custas 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 também haveria 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 preferisse, escrever código que fosse isomórfico ao Pascal. Se você não quiser expressar o fatorial em Arc como uma chamada a 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 imediato, estou interessado na questão de se uma linguagem poderia ser muito sucinta. Existem linguagens que forçam você a escrever código de uma maneira encolhida 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.)