A 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 a ajuda deles."
- Charles Babbage, citado na palestra de premiação Turing de Iverson
Na discussão sobre 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 posso perceber, concisão = poder. Se for assim, então substituindo, obtemos
O objetivo do Python é regularidade e legibilidade, não poder.
e isso não parece um compromisso (se for um compromisso) que você gostaria de fazer. Não está longe de dizer que o objetivo do Python não é ser eficaz como uma linguagem de programação.
A concisão = poder? Essa me parece uma pergunta importante, talvez a mais importante para qualquer um interessado em design de linguagens, e uma que seria útil confrontar diretamente. Não me sinto ainda seguro de que a resposta seja um simples sim, mas parece uma boa hipótese para começar.
Hipótese
Minha hipótese é que a concisão é poder, ou é suficientemente próxima que, exceto em exemplos patológicos, você pode tratá-las como idênticas.
Parece-me que a concisão é para isso que as linguagens de programação servem. Os computadores ficariam tão felizes em serem informados sobre o que fazer diretamente em linguagem de máquina. Acho que a principal razão pela qual nos damos ao 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 um código-fonte menor é o propósito das linguagens de alto nível, e o poder de algo é quão bem ele atinge seu propósito, então a medida do poder de uma linguagem de programação é quão pequeno ela torna seus programas.
Por outro lado, 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 que é ilegível.
Métricas
Pequeno em que sentido, porém? 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. Diferentes linguagens têm diferentes convenções sobre quanto você deve colocar em uma linha; em C, muitas linhas não têm nada nelas 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) simplesmente 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 número 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 cada linguagem, então 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 neste exercício é o que você tem que fazer em sua cabeça para conceber o programa, e assim 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 esse não é, pelo menos para mim, seu principal valor. O principal valor do teste de concisão é como um guia para desenhar 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 mais? E isso me parece idêntico a perguntar, como posso projetar uma boa linguagem?
(Incidentemente, nada torna mais patente 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 fosse realmente uma pergunta sem sentido, você poderia simplesmente jogar uma moeda.)
Atingir 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ê provavelmente descobriu uma nova abstração útil. Você pode até ser capaz de escrever um programa para ajudar buscando código-fonte por padrões repetidos. Entre outras linguagens, aquelas com uma reputação de concisão seriam as que você procuraria por novas ideias: Forth, Joy, Icon.
Comparação
A primeira pessoa a escrever sobre essas questões, pelo que sei, foi Fred Brooks em 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, aos meus vinte anos, foi uma grande surpresa para mim e parecia ter enormes implicações. Isso 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 dasse ao trabalho de fazer isso poderia deixar os concorrentes que não o fizessem para trás.
A hipótese de Brooks, se for verdadeira, parece estar no cerne da programação. Nos anos que se passaram, prestei atenção a qualquer evidência que pudesse obter sobre a questão, desde estudos formais até 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 gerem o tipo de resultados que eu esperava, tendem a usar problemas que são curtos demais 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 propósito de uma linguagem é ser boa para pensar (em vez de apenas dizer a um computador o que fazer uma vez que você tenha pensado nisso) é quais novas coisas você pode escrever nela. Portanto, qualquer comparação de linguagens onde você tem que atender a uma especificação pré-definida está testando ligeiramente a coisa errada.
O verdadeiro teste de uma linguagem é quão bem você pode descobrir e resolver novos problemas, nã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ê sabe de antemão o que deseja fazer, mas são absolutamente ruins se não souber. Quando você quer descobrir a imagem à medida que a cria-- como você tem que fazer com qualquer coisa tão complexa quanto a imagem de uma pessoa, por exemplo-- você precisa usar um meio mais fluido como lápis ou tinta aquosa ou tinta a óleo. E, de fato, a maneira como tapeçarias e mosaicos são feitos na prática é fazer uma pintura primeiro, e depois copiá-la. (A palavra "cartoon" 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 exatas. Em particular, estudos explícitos com o propósito de comparar linguagens, porque provavelmente usarão problemas pequenos, e necessariamente usarão problemas pré-definidos, tenderão a subestimar o poder das linguagens mais poderosas.
Relatos do campo, embora necessariamente menos precisos do que estudos "científicos", sã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:
Comparações entre projetos de desenvolvimento internos da Ericsson indicam produtividade semelhante em linhas/hora, incluindo todas as fases do desenvolvimento de software, de forma bastante independente 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 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 a produtividade do programador, em aplicações como switches de rede.
O Teste do Sabor
Em última análise, acho que você tem que seguir sua intuição. O que você sente ao programar na linguagem? Acho que a maneira de encontrar (ou projetar) a melhor linguagem é se tornar hipersensível a quão bem uma linguagem deixa você pensar, e então escolher/projetar a linguagem que parece melhor. Se algum recurso da linguagem é estranho 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. Eu acho insuportavelmente restritivo programar em linguagens sem macros, assim como alguém acostumado à tipagem dinâmica acha insuportavelmente restritivo ter que voltar a 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 diferentes tipos.
Não sou o único. Conheço muitos hackers de Lisp que passaram por isso. De fato, a medida mais precisa do poder relativo das linguagens de programação pode ser a porcentagem de pessoas que conhecem a linguagem que aceitarão qualquer trabalho onde possam usar essa linguagem, independentemente do domínio da aplicação.
Restritividade
Acho que a maioria dos hackers sabe o que significa uma linguagem parecer restritiva. O que está acontecendo quando você sente isso? Acho que é a mesma sensação que você tem quando a rua que deseja seguir está bloqueada, e você tem que fazer um longo desvio para chegar onde queria ir. Há algo que você quer dizer, e a linguagem não deixa você.
O que realmente está acontecendo aqui, eu acho, é que uma linguagem restritiva é aquela que não é concisa o suficiente. O problema não é simplesmente que você não pode dizer o que planejou. É que o desvio que a linguagem faz você tomar é mais longo. Tente este experimento mental. Suponha que haja algum programa que você queria escrever, e a linguagem não deixasse você expressá-lo da maneira que planejou, mas em vez disso o forçasse a escrever o programa de uma maneira que fosse mais curta. Para mim, pelo menos, isso não pareceria muito restritivo. Seria como a rua que você queria seguir sendo bloqueada, e o policial na interseção direcionando você 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 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 é suficientemente concisa, e quando uma linguagem não é concisa, ela parecerá restritiva.
Legibilidade
A citação com a qual comecei menciona duas outras 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 é meramente 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 do programa inteiro. É a segunda que importa. Concordo que uma linha de Basic é provavelmente 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 uma vez que você entre em Greenspunland). O esforço total de ler o programa em Basic será certamente 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 tenho de que o poder é, mas certamente a concisão é um fator (no sentido matemático; veja a equação acima) na legibilidade. Portanto, pode não ser nem mesmo 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 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. Assim, 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 permitir que as pessoas paguem em parcelas: em vez de assustá-las com um alto preço inicial, você diz a elas o baixo pagamento mensal. Os planos de parcelamento são uma perda líquida para o comprador, embora, assim como a mera legibilidade-por-linha provavelmente seja para o programador. O comprador vai fazer um monte desses pagamentos baixos; e o programador vai ler um monte dessas linhas individualmente legíveis.
Esse compromisso precede as linguagens de programação. Se você está acostumado a ler romances e artigos de jornal, sua primeira experiência de ler um artigo de matemática pode ser desanimadora. Pode levar meia hora para ler uma única página. E ainda assim, tenho certeza de que a notação não é o problema, mesmo que 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 tiveram que fazer antes de evoluírem 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?
Um número de pessoas rejeitou a ideia de que concisão = poder. Acho que seria mais útil, em vez de simplesmente argumentar que 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 servem. Se não é tudo para o que elas servem, então para que mais elas servem, 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 é que algum dia, uma linguagem é concisa demais para seu próprio bem?
A hipótese com a qual comecei foi que, exceto em exemplos patológicos, eu pensava que a concisão poderia ser considerada idêntica ao poder. O que quero dizer é 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 poderia fazê-lo. 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 de forma muito densa.
Escrevi sobre isso em On Lisp. Uma macro complexa pode ter que economizar muitas vezes seu próprio comprimento para ser justificada. Se escrever alguma macro complicada pudesse economizar dez linhas de código toda vez que você a usasse, e a macro tem ela mesma dez linhas de código, então você obtém uma economia líquida em linhas se a usar mais de uma vez. Mas isso ainda poderia ser um mau movimento, porque definições de macros são mais difíceis de ler do que código comum. Você pode ter que usar a macro dez ou vinte vezes antes que ela produza uma melhoria líquida em legibilidade.
Tenho certeza de que toda linguagem tem tais compromissos (embora eu suspeite que os riscos aumentem à medida que a linguagem se torna mais poderosa). Todo programador deve ter visto código que alguma pessoa inteligente tornou marginalmente mais curto usando truques de programação duvidosos.
Portanto, não há argumento sobre isso-- pelo menos, não da minha parte. Programas individuais podem certamente ser muito concisos para seu próprio bem. A questão é, uma linguagem pode ser? Uma linguagem pode compelir programadores a escrever código que seja curto (em elementos) à custa da legibilidade geral?
Uma razão pela qual é difícil imaginar uma linguagem sendo concisa demais é que se houvesse alguma maneira excessivamente compacta de expressar algo, provavelmente também haveria uma maneira mais longa. Por exemplo, se você sentisse que programas em Lisp usando muitas macros ou funções de ordem superior eram densos demais, você poderia, se preferisse, escrever código que fosse isomórfico ao Pascal. Se você não quiser expressar 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 eu não consiga pensar em nenhum exemplo de cabeça, estou interessado na questão de saber se uma linguagem poderia ser concisa demais. Existem linguagens que forçam você a escrever código de uma maneira que seja truncada e incompreensível? Se alguém tiver exemplos, eu 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 meramente programas que são curtos porque delimitadores podem ser omitidos e tudo tem um nome de um caractere.)
Fim do conteúdo.