Loading...

SER POPULAR

Original

Maio de 2001

(Este artigo foi escrito como uma espécie de plano de negócios para uma nova linguagem. Portanto, está faltando (porque presume) o recurso mais importante de uma boa linguagem de programação: abstrações muito poderosas.)

Um amigo meu uma vez disse a um eminente especialista em sistemas operacionais que ele queria projetar uma linguagem de programação realmente boa. O especialista disse a ele que seria um desperdício de tempo, que as linguagens de programação não se tornam populares ou impopulares com base em seus méritos, e que, não importa quão boa fosse sua linguagem, ninguém a usaria. Pelo menos, isso foi o que aconteceu com a linguagem que ele projetou.

O que faz uma linguagem ser popular? As linguagens populares merecem sua popularidade? Vale a pena tentar definir uma boa linguagem de programação? Como você faria isso?

Acho que as respostas a essas perguntas podem ser encontradas ao olhar para os hackers e aprender o que eles querem. As linguagens de programação são para hackers, e uma linguagem de programação é boa como uma linguagem de programação (em vez de, digamos, um exercício em semântica denotacional ou design de compiladores) se e somente se os hackers gostarem dela.

1 A Mecânica da Popularidade

É verdade, certamente, que a maioria das pessoas não escolhe linguagens de programação simplesmente com base em seus méritos. A maioria dos programadores é informada sobre qual linguagem usar por outra pessoa. E ainda assim, acho que o efeito de tais fatores externos sobre a popularidade das linguagens de programação não é tão grande quanto às vezes se pensa. Acho que um problema maior é que a ideia de um hacker sobre uma boa linguagem de programação não é a mesma que a de muitos designers de linguagem.

Entre os dois, a opinião do hacker é a que importa. As linguagens de programação não são teoremas. Elas são ferramentas, projetadas para pessoas, e devem ser projetadas para se adequar às forças e fraquezas humanas tanto quanto os sapatos devem ser projetados para os pés humanos. Se um sapato aperta quando você o coloca, é um sapato ruim, por mais elegante que possa ser como uma peça de escultura.

Pode ser que a maioria dos programadores não consiga distinguir uma boa linguagem de uma ruim. Mas isso não é diferente de qualquer outra ferramenta. Isso não significa que seja um desperdício de tempo tentar projetar uma boa linguagem. Hackers especialistas podem reconhecer uma boa linguagem quando a veem, e eles a usarão. Hackers especialistas são uma minoria minúscula, admitidamente, mas essa minúscula minoria escreve todo o bom software, e sua influência é tal que o restante dos programadores tende a usar qualquer que seja a linguagem que eles usam. Muitas vezes, de fato, não é apenas influência, mas comando: muitas vezes os hackers especialistas são as próprias pessoas que, como seus chefes ou orientadores acadêmicos, dizem aos outros programadores qual linguagem usar.

A opinião dos hackers especialistas não é a única força que determina a popularidade relativa das linguagens de programação — software legado (Cobol) e hype (Ada, Java) também desempenham um papel — mas acho que é a força mais poderosa a longo prazo. Dada uma massa crítica inicial e tempo suficiente, uma linguagem de programação provavelmente se torna tão popular quanto merece ser. E a popularidade separa ainda mais boas linguagens de más, porque o feedback de usuários reais sempre leva a melhorias. Veja quanto qualquer linguagem popular mudou durante sua vida. Perl e Fortran são casos extremos, mas até mesmo Lisp mudou muito. Lisp 1.5 não tinha macros, por exemplo; essas evoluíram mais tarde, depois que hackers do MIT passaram alguns anos usando Lisp para escrever programas reais. [1]

Então, quer uma linguagem tenha que ser boa para ser popular, eu acho que uma linguagem tem que ser popular para ser boa. E ela tem que continuar popular para continuar boa. O estado da arte em linguagens de programação não permanece parado. E ainda assim, os Lisps que temos hoje são praticamente o que tinham no MIT em meados da década de 1980, porque essa foi a última vez que Lisp teve uma base de usuários suficientemente grande e exigente.

Claro, os hackers têm que conhecer uma linguagem antes de poderem usá-la. Como eles vão ouvir? De outros hackers. Mas deve haver algum grupo inicial de hackers usando a linguagem para que outros possam até ouvir sobre ela. Eu me pergunto quão grande esse grupo precisa ser; quantos usuários fazem uma massa crítica? De cabeça, eu diria vinte. Se uma linguagem tivesse vinte usuários separados, significando vinte usuários que decidiram por conta própria usá-la, eu consideraria que ela é real.

Chegar lá não pode ser fácil. Eu não ficaria surpreso se fosse mais difícil ir de zero a vinte do que de vinte a mil. A melhor maneira de conseguir esses vinte usuários iniciais é provavelmente usar um cavalo de Troia: dar às pessoas um aplicativo que elas querem, que é escrito na nova linguagem.

2 Fatores Externos

Vamos começar reconhecendo um fator externo que afeta a popularidade de uma linguagem de programação. Para se tornar popular, uma linguagem de programação tem que ser a linguagem de script de um sistema popular. Fortran e Cobol eram as linguagens de script dos primeiros mainframes da IBM. C foi a linguagem de script do Unix, e assim, mais tarde, foi Perl. Tcl é a linguagem de script do Tk. Java e Javascript são destinadas a ser as linguagens de script dos navegadores web.

Lisp não é uma linguagem massivamente popular porque não é a linguagem de script de um sistema massivamente popular. A popularidade que ela mantém remonta às décadas de 1960 e 1970, quando era a linguagem de script do MIT. Muitos dos grandes programadores da época estavam associados ao MIT em algum momento. E no início da década de 1970, antes de C, o dialeto de Lisp do MIT, chamado MacLisp, era uma das poucas linguagens de programação que um hacker sério gostaria de usar.

Hoje, Lisp é a linguagem de script de dois sistemas moderadamente populares, Emacs e Autocad, e por essa razão suspeito que a maior parte da programação em Lisp feita hoje é feita em Emacs Lisp ou AutoLisp.

As linguagens de programação não existem em isolamento. Hackear é um verbo transitivo — hackers geralmente estão hackeando algo — e na prática as linguagens são julgadas em relação ao que quer que estejam usando para hackear. Então, se você quer projetar uma linguagem popular, você ou tem que fornecer mais do que uma linguagem, ou tem que projetar sua linguagem para substituir a linguagem de script de algum sistema existente.

Common Lisp é impopular em parte porque é um órfão. Ela originalmente veio com um sistema para hackear: a Máquina Lisp. Mas as Máquinas Lisp (junto com computadores paralelos) foram esmagadas pelo poder crescente dos processadores de propósito geral na década de 1980. Common Lisp poderia ter permanecido popular se tivesse sido uma boa linguagem de script para Unix. É, infelizmente, uma linguagem horrivelmente ruim.

Uma maneira de descrever essa situação é dizer que uma linguagem não é julgada por seus próprios méritos. Outra visão é que uma linguagem de programação realmente não é uma linguagem de programação a menos que também seja a linguagem de script de algo. Isso só parece injusto se vier como uma surpresa. Acho que não é mais injusto do que esperar que uma linguagem de programação tenha, digamos, uma implementação. É apenas parte do que uma linguagem de programação é.

Uma linguagem de programação precisa de uma boa implementação, é claro, e isso deve ser gratuito. As empresas pagarão por software, mas hackers individuais não pagarão, e são os hackers que você precisa atrair.

Uma linguagem também precisa ter um livro sobre ela. O livro deve ser fino, bem escrito e cheio de bons exemplos. K&R é o ideal aqui. No momento, eu diria quase que uma linguagem precisa ter um livro publicado pela O'Reilly. Isso está se tornando o teste de relevância para os hackers.

Deve haver documentação online também. Na verdade, o livro pode começar como documentação online. Mas eu não acho que livros físicos estão obsoletos ainda. Seu formato é conveniente, e a censura de fato imposta pelos editores é um filtro útil, embora imperfeito. As livrarias são um dos lugares mais importantes para aprender sobre novas linguagens.

3 Brevidade

Dado que você pode fornecer as três coisas que qualquer linguagem precisa — uma implementação gratuita, um livro e algo para hackear — como você faz uma linguagem que os hackers vão gostar?

Uma coisa que os hackers gostam é brevidade. Hackers são preguiçosos, da mesma forma que matemáticos e arquitetos modernistas são preguiçosos: eles odeiam qualquer coisa extranha. Não seria longe da verdade dizer que um hacker prestes a escrever um programa decide qual linguagem usar, pelo menos subconscientemente, com base no número total de caracteres que ele terá que digitar. Se isso não é precisamente como os hackers pensam, um designer de linguagem faria bem em agir como se fosse.

É um erro tentar mimar o usuário com expressões prolixas que pretendem se assemelhar ao inglês. Cobol é notório por esse defeito. Um hacker consideraria ser solicitado a escrever

add x to y giving z

em vez de

z = x+y

como algo entre um insulto à sua inteligência e um pecado contra Deus.

Às vezes, foi dito que Lisp deveria usar first e rest em vez de car e cdr, porque isso tornaria os programas mais fáceis de ler. Talvez nas primeiras horas. Mas um hacker pode aprender rapidamente que car significa o primeiro elemento de uma lista e cdr significa o resto. Usar first e rest significa 50% mais digitação. E eles também têm comprimentos diferentes, o que significa que os argumentos não alinhariam quando fossem chamados, como car e cdr costumam ser, em linhas sucessivas. Eu descobri que importa muito como o código se alinha na página. Eu mal consigo ler código Lisp quando está configurado em uma fonte de largura variável, e amigos dizem que isso é verdade para outras linguagens também.

A brevidade é um lugar onde linguagens fortemente tipadas perdem. Todas as outras coisas sendo iguais, ninguém quer começar um programa com um monte de declarações. Qualquer coisa que possa ser implícita, deve ser.

Os tokens individuais também devem ser curtos. Perl e Common Lisp ocupam polos opostos nessa questão. Programas Perl podem ser quase crypticamente densos, enquanto os nomes dos operadores embutidos do Common Lisp são cômicamente longos. Os designers do Common Lisp provavelmente esperavam que os usuários tivessem editores de texto que digitassem esses longos nomes para eles. Mas o custo de um nome longo não é apenas o custo de digitá-lo. Há também o custo de lê-lo, e o custo do espaço que ocupa na sua tela.

4 Hackabilidade

Há uma coisa mais importante do que brevidade para um hacker: ser capaz de fazer o que você quer. Na história das linguagens de programação, uma quantidade surpreendente de esforço foi dedicada a impedir que os programadores fizessem coisas consideradas impróprias. Este é um plano perigosamente presunçoso. Como o designer da linguagem pode saber o que o programador vai precisar fazer? Acho que os designers de linguagem fariam melhor em considerar seu usuário-alvo como um gênio que precisará fazer coisas que nunca anteciparam, em vez de um atrapalhado que precisa ser protegido de si mesmo. O atrapalhado vai se machucar de qualquer maneira. Você pode salvá-lo de se referir a variáveis em outro pacote, mas não pode salvá-lo de escrever um programa mal projetado para resolver o problema errado, e levar uma eternidade para fazê-lo.

Programadores bons muitas vezes querem fazer coisas perigosas e desagradáveis. Por desagradáveis, quero dizer coisas que vão além de qualquer fachada semântica que a linguagem está tentando apresentar: obter a representação interna de alguma abstração de alto nível, por exemplo. Hackers gostam de hackear, e hackear significa entrar nas coisas e adivinhar o que o designer original pretendia.

Deixe-se ser adivinhado. Quando você faz qualquer ferramenta, as pessoas a usam de maneiras que você não pretendia, e isso é especialmente verdadeiro para uma ferramenta altamente articulada como uma linguagem de programação. Muitos hackers querem ajustar seu modelo semântico de uma maneira que você nunca imaginou. Eu digo, deixe-os; dê ao programador acesso ao máximo de informações internas que você puder sem colocar em risco sistemas de tempo de execução como o coletor de lixo.

No Common Lisp, muitas vezes quis iterar pelos campos de uma struct — para eliminar referências a um objeto deletado, por exemplo, ou encontrar campos que estão não inicializados. Eu sei que as structs são apenas vetores por baixo. E ainda assim não consigo escrever uma função de propósito geral que eu possa chamar em qualquer struct. Eu só posso acessar os campos pelo nome, porque é isso que uma struct deve significar.

Um hacker pode querer subverter o modelo pretendido das coisas apenas uma ou duas vezes em um grande programa. Mas que diferença faz poder fazê-lo. E pode ser mais do que uma questão de apenas resolver um problema. Há um tipo de prazer aqui também. Hackers compartilham o prazer secreto do cirurgião em mexer nas entranhas, o prazer secreto do adolescente em estourar espinhas. [2] Para meninos, pelo menos, certos tipos de horrores são fascinantes. A revista Maxim publica um volume anual de fotografias, contendo uma mistura de pin-ups e acidentes horríveis. Eles conhecem seu público.

Historicamente, Lisp tem sido boa em deixar os hackers fazerem o que querem. A correção política do Common Lisp é uma aberração. Os primeiros Lisps deixavam você ter acesso a tudo. Uma boa parte desse espírito é, felizmente, preservada em macros. Que coisa maravilhosa, poder fazer transformações arbitrárias no código fonte.

Macros clássicas são uma verdadeira ferramenta de hacker — simples, poderosas e perigosas. É tão fácil entender o que elas fazem: você chama uma função nos argumentos da macro, e o que quer que ela retorne é inserido no lugar da chamada da macro. Macros higiênicas incorporam o princípio oposto. Elas tentam proteger você de entender o que estão fazendo. Eu nunca ouvi macros higiênicas serem explicadas em uma frase. E elas são um exemplo clássico dos perigos de decidir o que os programadores têm permissão para querer. Macros higiênicas são destinadas a me proteger de captura de variável, entre outras coisas, mas a captura de variável é exatamente o que eu quero em algumas macros.

Uma linguagem realmente boa deve ser tanto limpa quanto suja: limpa em design, com um pequeno núcleo de operadores bem compreendidos e altamente ortogonais, mas suja no sentido de que permite que os hackers façam o que quiserem com ela. C é assim. Os primeiros Lisps também eram. A linguagem de um verdadeiro hacker sempre terá um caráter ligeiramente desregrado.

Uma boa linguagem de programação deve ter recursos que fazem com que o tipo de pessoas que usam a frase "engenharia de software" balancem a cabeça desaprovando. No outro extremo do espectro estão linguagens como Ada e Pascal, modelos de propriedade que são bons para ensinar e não muito mais.

5 Programas Descartáveis

Para ser atraente para hackers, uma linguagem deve ser boa para escrever os tipos de programas que eles querem escrever. E isso significa, talvez surpreendentemente, que ela tem que ser boa para escrever programas descartáveis.

Um programa descartável é um programa que você escreve rapidamente para alguma tarefa limitada: um programa para automatizar alguma tarefa de administração de sistema, ou gerar dados de teste para uma simulação, ou converter dados de um formato para outro. A coisa surpreendente sobre programas descartáveis é que, como os edifícios "temporários" construídos em tantas universidades americanas durante a Segunda Guerra Mundial, eles muitas vezes não são descartados. Muitos evoluem para programas reais, com recursos reais e usuários reais.

Eu tenho a intuição de que os melhores grandes programas começam a vida dessa maneira, em vez de serem projetados grandes desde o início, como a Represa Hoover. É aterrorizante construir algo grande do zero. Quando as pessoas assumem um projeto que é grande demais, elas ficam sobrecarregadas. O projeto ou fica atolado, ou o resultado é estéril e sem vida: um shopping center em vez de um verdadeiro centro da cidade, Brasília em vez de Roma, Ada em vez de C.

Outra maneira de obter um grande programa é começar com um programa descartável e continuar melhorando-o. Essa abordagem é menos assustadora, e o design do programa se beneficia da evolução. Eu acho que, se alguém olhasse, isso acabaria sendo a maneira como a maioria dos grandes programas foi desenvolvida. E aqueles que evoluíram dessa maneira provavelmente ainda estão escritos na linguagem em que foram escritos pela primeira vez, porque é raro que um programa seja portado, exceto por razões políticas. E assim, paradoxalmente, se você quiser fazer uma linguagem que seja usada para grandes sistemas, você tem que torná-la boa para escrever programas descartáveis, porque é daí que vêm os grandes sistemas.

Perl é um exemplo marcante dessa ideia. Não foi apenas projetado para escrever programas descartáveis, mas era praticamente um programa descartável em si. Perl começou a vida como uma coleção de utilitários para gerar relatórios, e só evoluiu para uma linguagem de programação à medida que os programas descartáveis que as pessoas escreviam nela cresciam. Não foi até o Perl 5 (se é que foi) que a linguagem se tornou adequada para escrever programas sérios, e ainda assim já era massivamente popular.

O que torna uma linguagem boa para programas descartáveis? Para começar, ela deve estar prontamente disponível. Um programa descartável é algo que você espera escrever em uma hora. Portanto, a linguagem provavelmente deve já estar instalada no computador que você está usando. Não pode ser algo que você tenha que instalar antes de usá-la. Ela tem que estar lá. C estava lá porque veio com o sistema operacional. Perl estava lá porque era originalmente uma ferramenta para administradores de sistema, e o seu já a havia instalado.

Estar disponível significa mais do que estar instalada, no entanto. Uma linguagem interativa, com uma interface de linha de comando, é mais disponível do que uma que você tem que compilar e executar separadamente. Uma linguagem de programação popular deve ser interativa e iniciar rapidamente.

Outra coisa que você quer em um programa descartável é brevidade. A brevidade é sempre atraente para hackers, e nunca mais do que em um programa que eles esperam produzir em uma hora.

6 Bibliotecas

Claro que o ideal em brevidade é ter o programa já escrito para você, e meramente chamá-lo. E isso nos traz ao que acho que será um recurso cada vez mais importante das linguagens de programação: funções de biblioteca. Perl vence porque tem grandes bibliotecas para manipulação de strings. Essa classe de funções de biblioteca é especialmente importante para programas descartáveis, que muitas vezes são originalmente escritos para converter ou extrair dados. Muitos programas Perl provavelmente começam como apenas algumas chamadas de biblioteca coladas juntas.

Acho que muitos dos avanços que acontecerão nas linguagens de programação nos próximos cinquenta anos terão a ver com funções de biblioteca. Eu acho que as futuras linguagens de programação terão bibliotecas que são tão cuidadosamente projetadas quanto a linguagem central. O design de linguagem de programação não será sobre se fazer sua linguagem fortemente ou fracamente tipada, ou orientada a objetos, ou funcional, ou o que for, mas sobre como projetar grandes bibliotecas. O tipo de designers de linguagem que gostam de pensar sobre como projetar sistemas de tipos pode estremecer com isso. É quase como escrever aplicações! Que pena. Linguagens são para programadores, e bibliotecas são o que os programadores precisam.

É difícil projetar boas bibliotecas. Não é simplesmente uma questão de escrever muito código. Uma vez que as bibliotecas ficam grandes demais, pode às vezes levar mais tempo para encontrar a função que você precisa do que para escrever o código você mesmo. As bibliotecas precisam ser projetadas usando um pequeno conjunto de operadores ortogonais, assim como a linguagem central. Deve ser possível para o programador adivinhar qual chamada de biblioteca fará o que ele precisa.

As bibliotecas são um lugar onde o Common Lisp fica aquém. Existem apenas bibliotecas rudimentares para manipulação de strings, e quase nenhuma para comunicação com o sistema operacional. Por razões históricas, o Common Lisp tenta fingir que o SO não existe. E porque você não pode falar com o SO, é improvável que você consiga escrever um programa sério usando apenas os operadores embutidos no Common Lisp. Você tem que usar alguns hacks específicos da implementação também, e na prática esses tendem a não lhe dar tudo o que você quer. Os hackers pensariam muito mais positivamente sobre Lisp se o Common Lisp tivesse bibliotecas de strings poderosas e um bom suporte ao SO.

7 Sintaxe

Uma linguagem com a sintaxe de Lisp, ou mais precisamente, a falta de sintaxe, poderia algum dia se tornar popular? Eu não sei a resposta para essa pergunta. Eu realmente acho que a sintaxe não é a principal razão pela qual Lisp não é atualmente popular. O Common Lisp tem problemas piores do que uma sintaxe desconhecida. Eu conheço vários programadores que estão confortáveis com a sintaxe prefixada e ainda assim usam Perl por padrão, porque tem bibliotecas de strings poderosas e pode se comunicar com o SO.

Existem dois problemas possíveis com a notação prefixada: que ela é desconhecida para os programadores, e que não é densa o suficiente. A sabedoria convencional no mundo Lisp é que o primeiro problema é o verdadeiro. Eu não tenho tanta certeza. Sim, a notação prefixada faz programadores comuns entrarem em pânico. Mas eu não acho que as opiniões dos programadores comuns importem. As linguagens se tornam populares ou impopulares com base no que os hackers especialistas pensam delas, e eu acho que os hackers especialistas podem ser capazes de lidar com a notação prefixada. A sintaxe do Perl pode ser bastante incompreensível, mas isso não impediu a popularidade do Perl. Se algo, pode ter ajudado a fomentar um culto ao Perl.

Um problema mais sério é a difusão da notação prefixada. Para hackers especialistas, isso realmente é um problema. Ninguém quer escrever (aref a x y) quando poderia escrever a[x,y].

Neste caso específico, há uma maneira de contornar o problema. Se tratarmos estruturas de dados como se fossem funções em índices, poderíamos escrever (a x y) em vez disso, que é ainda mais curto do que a forma do Perl. Truques semelhantes podem encurtar outros tipos de expressões.

Podemos nos livrar de (ou torná-las opcionais) muitas parênteses tornando a indentação significativa. É assim que os programadores leem código de qualquer maneira: quando a indentação diz uma coisa e os delimitadores dizem outra, seguimos a indentação. Tratar a indentação como significativa eliminaria essa fonte comum de bugs, além de tornar os programas mais curtos.

Às vezes, a sintaxe infixa é mais fácil de ler. Isso é especialmente verdadeiro para expressões matemáticas. Eu usei Lisp toda a minha vida de programação e ainda não acho expressões matemáticas prefixadas naturais. E ainda assim é conveniente, especialmente quando você está gerando código, ter operadores que aceitam qualquer número de argumentos. Então, se tivermos sintaxe infixa, provavelmente deve ser implementada como algum tipo de macro de leitura.

Eu não acho que devamos ser religiosamente opostos à introdução de sintaxe no Lisp, desde que se traduza de uma maneira bem compreendida em expressões s. Já existe uma boa quantidade de sintaxe em Lisp. Não é necessariamente ruim introduzir mais, desde que ninguém seja forçado a usá-la. No Common Lisp, alguns delimitadores são reservados para a linguagem, sugerindo que pelo menos alguns dos designers pretendiam ter mais sintaxe no futuro.

Um dos pedaços de sintaxe mais egregiamente não-lispianos no Common Lisp ocorre em strings de formato; format é uma linguagem por si só, e essa linguagem não é Lisp. Se houvesse um plano para introduzir mais sintaxe no Lisp, especificadores de formato poderiam ser incluídos nele. Seria uma boa coisa se macros pudessem gerar especificadores de formato da mesma forma que geram qualquer outro tipo de código.

Um eminente hacker de Lisp me disse que sua cópia do CLTL se abre na seção de formato. A minha também. Isso provavelmente indica espaço para melhoria. Pode também significar que os programas fazem muito I/O.

8 Eficiência

Uma boa linguagem, como todos sabem, deve gerar código rápido. Mas na prática, eu não acho que código rápido venha principalmente de coisas que você faz no design da linguagem. Como Knuth apontou há muito tempo, a velocidade só importa em certos gargalos críticos. E como muitos programadores observaram desde então, muitas vezes estamos muito enganados sobre onde esses gargalos estão.

Então, na prática, a maneira de obter código rápido é ter um bom profiler, em vez de, digamos, fazer a linguagem fortemente tipada. Você não precisa saber o tipo de cada argumento em cada chamada no programa. Você precisa ser capaz de declarar os tipos de argumentos nos gargalos. E ainda mais, você precisa ser capaz de descobrir onde estão os gargalos.

Uma reclamação que as pessoas têm sobre Lisp é que é difícil saber o que é caro. Isso pode ser verdade. Também pode ser inevitável, se você quiser ter uma linguagem muito abstrata. E de qualquer forma, eu acho que uma boa profilagem ajudaria muito a resolver o problema: você logo aprenderia o que era caro.

Parte do problema aqui é social. Designers de linguagem gostam de escrever compiladores rápidos. É assim que eles medem sua habilidade. Eles pensam no profiler como um complemento, na melhor das hipóteses. Mas na prática, um bom profiler pode fazer mais para melhorar a velocidade dos programas reais escritos na linguagem do que um compilador que gera código rápido. Aqui, novamente, os designers de linguagem estão um pouco desconectados de seus usuários. Eles fazem um trabalho realmente bom de resolver um problema um pouco errado.

Pode ser uma boa ideia ter um profiler ativo — para empurrar dados de desempenho para o programador em vez de esperar que ele venha pedir. Por exemplo, o editor poderia exibir gargalos em vermelho quando o programador edita o código fonte. Outra abordagem seria de alguma forma representar o que está acontecendo em programas em execução. Isso seria uma grande vitória em aplicações baseadas em servidor, onde você tem muitos programas em execução para observar. Um profiler ativo poderia mostrar graficamente o que está acontecendo na memória enquanto um programa está em execução, ou até mesmo fazer sons que indicam o que está acontecendo.

O som é uma boa pista para problemas. Em um lugar onde trabalhei, tínhamos um grande painel de mostradores mostrando o que estava acontecendo com nossos servidores web. Os ponteiros eram movidos por pequenos servomotores que faziam um leve barulho quando giravam. Eu não conseguia ver o painel da minha mesa, mas descobri que podia dizer imediatamente, pelo som, quando havia um problema com um servidor.

Pode até ser possível escrever um profiler que detectasse automaticamente algoritmos ineficientes. Eu não ficaria surpreso se certos padrões de acesso à memória se revelassem sinais seguros de algoritmos ruins. Se houvesse um pequeno cara correndo dentro do computador executando nossos programas, ele provavelmente teria uma história tão longa e lamentável para contar sobre seu trabalho quanto um funcionário do governo federal. Muitas vezes tenho a sensação de que estou enviando o processador em muitas perseguições inúteis, mas nunca tive uma boa maneira de ver o que ele está fazendo.

Vários Lisps agora compilam em bytecode, que é então executado por um interpretador. Isso geralmente é feito para facilitar a portabilidade da implementação, mas poderia ser um recurso útil da linguagem. Pode ser uma boa ideia tornar o bytecode uma parte oficial da linguagem, e permitir que programadores usem bytecode inline em gargalos. Então, tais otimizações também seriam portáveis.

A natureza da velocidade, como percebida pelo usuário final, pode estar mudando. Com o aumento das aplicações baseadas em servidor, mais e mais programas podem acabar sendo limitados por I/O. Vale a pena tornar o I/O rápido. A linguagem pode ajudar com medidas diretas como funções de saída formatadas simples e rápidas, e também com mudanças estruturais profundas como caching e objetos persistentes.

Os usuários estão interessados no tempo de resposta. Mas outro tipo de eficiência será cada vez mais importante: o número de usuários simultâneos que você pode suportar por processador. Muitas das aplicações interessantes escritas no futuro próximo serão baseadas em servidor, e o número de usuários por servidor é a questão crítica para qualquer um que hospede tais aplicações. No custo de capital de um negócio que oferece uma aplicação baseada em servidor, este é o divisor.

Por anos, a eficiência não importou muito na maioria das aplicações para o usuário final. Os desenvolvedores puderam assumir que cada usuário teria um processador cada vez mais poderoso em sua mesa. E pela Lei de Parkinson, o software se expandiu para usar os recursos disponíveis. Isso mudará com aplicações baseadas em servidor. Nesse mundo, o hardware e o software serão fornecidos juntos. Para empresas que oferecem aplicações baseadas em servidor, fará uma grande diferença para o resultado final quantos usuários eles podem suportar por servidor.

Em algumas aplicações, o processador será o fator limitante, e a velocidade de execução será a coisa mais importante a otimizar. Mas muitas vezes a memória será o limite; o número de usuários simultâneos será determinado pela quantidade de memória que você precisa para os dados de cada usuário. A linguagem pode ajudar aqui também. Um bom suporte para threads permitirá que todos os usuários compartilhem um único heap. Pode também ajudar ter objetos persistentes e/ou suporte a nível de linguagem para carregamento preguiçoso.

9 Tempo

O último ingrediente que uma linguagem popular precisa é tempo. Ninguém quer escrever programas em uma linguagem que pode desaparecer, como tantas linguagens de programação fazem. Portanto, a maioria dos hackers tende a esperar até que uma linguagem tenha estado por aí por alguns anos antes de considerar usá-la.

Inventores de coisas novas e maravilhosas muitas vezes ficam surpresos ao descobrir isso, mas você precisa de tempo para transmitir qualquer mensagem às pessoas. Um amigo meu raramente faz algo na primeira vez que alguém lhe pede. Ele sabe que as pessoas às vezes pedem coisas que acabam não querendo. Para evitar desperdiçar seu tempo, ele espera até a terceira ou quarta vez que lhe pedem para fazer algo; até lá, quem está pedindo pode estar bastante irritado, mas pelo menos eles provavelmente realmente querem o que estão pedindo.

A maioria das pessoas aprendeu a fazer um tipo semelhante de filtragem em novas coisas que ouvem. Elas nem começam a prestar atenção até que tenham ouvido sobre algo dez vezes. Elas estão perfeitamente justificadas: a maioria das novidades quentes acaba se revelando um desperdício de tempo e eventualmente desaparece. Ao atrasar o aprendizado de VRML, evitei ter que aprendê-lo completamente.

Então, qualquer um que inventa algo novo tem que esperar repetir sua mensagem por anos antes que as pessoas comecem a entendê-la. Nós escrevemos o que foi, até onde sei, a primeira aplicação baseada em servidor web, e levou anos para que as pessoas entendessem que não precisava ser baixada. Não era que elas fossem estúpidas. Elas simplesmente nos ignoraram.

A boa notícia é que a repetição simples resolve o problema. Tudo o que você precisa fazer é continuar contando sua história, e eventualmente as pessoas começarão a ouvir. Não é quando as pessoas notam que você está lá que elas prestam atenção; é quando elas notam que você ainda está lá.

É bom que geralmente leve um tempo para ganhar impulso. A maioria das tecnologias evolui bastante mesmo depois de serem lançadas pela primeira vez — linguagens de programação especialmente. Nada poderia ser melhor, para uma nova tecnologia, do que alguns anos sendo usada apenas por um pequeno número de primeiros adotantes. Os primeiros adotantes são sofisticados e exigentes, e rapidamente eliminam quaisquer falhas que permaneçam em sua tecnologia. Quando você tem apenas alguns usuários, pode estar em contato próximo com todos eles. E os primeiros adotantes são indulgentes quando você melhora seu sistema, mesmo que isso cause algumas quebras.

Existem duas maneiras de introduzir nova tecnologia: o método de crescimento orgânico e o método do grande estrondo. O método de crescimento orgânico é exemplificado pela clássica startup de garagem subfinanciada e improvisada. Alguns caras, trabalhando na obscuridade, desenvolvem alguma nova tecnologia. Eles a lançam sem marketing e inicialmente têm apenas alguns (fanaticamente devotados) usuários. Eles continuam a melhorar a tecnologia, e enquanto isso sua base de usuários cresce boca a boca. Antes que percebam, eles são grandes.

A outra abordagem, o método do grande estrondo, é exemplificada pela startup apoiada por capital de risco, fortemente comercializada. Eles correm para desenvolver um produto, lançam-no com grande publicidade e imediatamente (eles esperam) têm uma grande base de usuários.

Geralmente, os caras da garagem invejam os caras do grande estrondo. Os caras do grande estrondo são suaves e confiantes e respeitados pelos VCs. Eles podem pagar pelo melhor de tudo, e a campanha de relações públicas em torno do lançamento tem o efeito colateral de torná-los celebridades. Os caras do crescimento orgânico, sentados em suas garagens, se sentem pobres e não amados. E ainda assim, acho que muitas vezes eles estão errados em se sentir lamentáveis. O crescimento orgânico parece gerar melhor tecnologia e fundadores mais ricos do que o método do grande estrondo. Se você olhar para as tecnologias dominantes hoje, descobrirá que a maioria delas cresceu organicamente.

Esse padrão não se aplica apenas a empresas. Você o vê em pesquisas patrocinadas também. Multics e Common Lisp foram projetos de grande estrondo, e Unix e MacLisp foram projetos de crescimento orgânico.

10 Redesign

"A melhor escrita é reescrita," escreveu E. B. White. Todo bom escritor sabe disso, e é verdade para software também. A parte mais importante do design é o redesign. Linguagens de programação, especialmente, não são redesenhadas o suficiente.

Para escrever um bom software, você deve manter simultaneamente duas ideias opostas em sua cabeça. Você precisa da fé ingênua do jovem hacker em suas habilidades, e ao mesmo tempo do ceticismo do veterano. Você tem que ser capaz de pensar quão difícil pode ser? com uma metade do seu cérebro enquanto pensa nunca vai funcionar com a outra.

O truque é perceber que não há uma verdadeira contradição aqui. Você quer ser otimista e cético sobre duas coisas diferentes. Você tem que ser otimista sobre a possibilidade de resolver o problema, mas cético sobre o valor de qualquer solução que você tenha até agora.

Pessoas que fazem um bom trabalho muitas vezes pensam que o que quer que estejam trabalhando não é bom. Outros veem o que fizeram e estão cheios de admiração, mas o criador está cheio de preocupação. Esse padrão não é uma coincidência: é a preocupação que tornou o trabalho bom.

Se você conseguir manter a esperança e a preocupação equilibradas, elas impulsionarão um projeto para frente da mesma forma que suas duas pernas impulsionam uma bicicleta para frente. Na primeira fase do motor de inovação de dois ciclos, você trabalha furiosamente em algum problema, inspirado pela sua confiança de que conseguirá resolvê-lo. Na segunda fase, você olha para o que fez à luz fria da manhã e vê todos os seus defeitos muito claramente. Mas enquanto seu espírito crítico não superar sua esperança, você será capaz de olhar para seu sistema, admitidamente incompleto, e pensar, quão difícil pode ser chegar ao resto do caminho?, continuando assim o ciclo.

É complicado manter as duas forças equilibradas. Nos hackers jovens, o otimismo predomina. Eles produzem algo, estão convencidos de que é ótimo e nunca o melhoram. Nos hackers mais velhos, o ceticismo predomina, e eles nem se atreverão a assumir projetos ambiciosos.

Qualquer coisa que você possa fazer para manter o ciclo de redesign em andamento é boa. A prosa pode ser reescrita repetidamente até que você esteja satisfeito com ela. Mas software, como regra, não é redesenhado o suficiente. A prosa tem leitores, mas o software tem usuários. Se um escritor reescreve um ensaio, as pessoas que leram a versão antiga provavelmente não se queixarão de que seus pensamentos foram quebrados por alguma incompatibilidade recém-introduzida.

Usuários são uma espada de dois gumes. Eles podem ajudá-lo a melhorar sua linguagem, mas também podem desencorajá-lo de melhorá-la. Então escolha seus usuários com cuidado e seja lento para aumentar seu número. Ter usuários é como otimização: o curso sábio é adiá-la. Além disso, como regra geral, você pode a qualquer momento mudar mais do que pensa. Introduzir mudanças é como arrancar um band-aid: a dor é uma memória quase tão logo você a sente.

Todo mundo sabe que não é uma boa ideia ter uma linguagem projetada por um comitê. Comitês geram um mau design. Mas eu acho que o pior perigo dos comitês é que eles interferem no redesign. É tanto trabalho introduzir mudanças que ninguém quer se dar ao trabalho. O que quer que um comitê decida tende a permanecer assim, mesmo que a maioria dos membros não goste.

Até mesmo um comitê de dois se interpõe ao redesign. Isso acontece particularmente nas interfaces entre peças de software escritas por duas pessoas diferentes. Para mudar a interface, ambas têm que concordar em mudá-la ao mesmo tempo. E assim as interfaces tendem a não mudar nada, o que é um problema porque tendem a ser uma das partes mais ad hoc de qualquer sistema.

Uma solução aqui pode ser projetar sistemas de modo que as interfaces sejam horizontais em vez de verticais — de modo que os módulos sejam sempre estratos de abstração empilhados verticalmente. Então a interface tenderá a ser propriedade de um deles. O nível inferior de dois níveis será ou uma linguagem na qual o superior é escrito, caso em que o nível inferior possuirá a interface, ou será um escravo, caso em que a interface pode ser ditada pelo nível superior.

11 Lisp

O que tudo isso implica é que há esperança para um novo Lisp. Há esperança para qualquer linguagem que dê aos hackers o que eles querem, incluindo Lisp. Acho que podemos ter cometido um erro ao pensar que os hackers se afastam da estranheza do Lisp. Essa ilusão confortante pode ter nos impedido de ver o verdadeiro problema com o Lisp, ou pelo menos com o Common Lisp, que é que ele é ruim para fazer o que os hackers querem fazer. A linguagem de um hacker precisa de bibliotecas poderosas e algo para hackear. O Common Lisp não tem nenhuma das duas. A linguagem de um hacker é concisa e hackeável. O Common Lisp não é.

A boa notícia é que não é o Lisp que é ruim, mas o Common Lisp. Se conseguirmos desenvolver um novo Lisp que seja uma verdadeira linguagem de hacker, acho que os hackers o usarão. Eles usarão qualquer linguagem que faça o trabalho. Tudo o que precisamos fazer é garantir que esse novo Lisp faça algum trabalho importante melhor do que outras linguagens.

A história oferece algum encorajamento. Com o tempo, novas linguagens de programação sucessivas têm incorporado cada vez mais recursos do Lisp. Não há mais muito o que copiar antes que a linguagem que você criou seja Lisp. A última linguagem em alta, Python, é um Lisp diluído com sintaxe infixa e sem macros. Um novo Lisp seria um passo natural nessa progressão.

Às vezes, penso que seria um bom truque de marketing chamá-lo de uma versão melhorada do Python. Isso soa mais moderno do que Lisp. Para muitas pessoas, Lisp é uma linguagem de IA lenta com muitas parênteses. A biografia oficial de Fritz Kunze evita cuidadosamente mencionar a palavra L. Mas meu palpite é que não devemos ter medo de chamar o novo Lisp de Lisp. O Lisp ainda tem muito respeito latente entre os melhores hackers — aqueles que fizeram 6.001 e entenderam, por exemplo. E esses são os usuários que você precisa conquistar.

Em "Como se Tornar um Hacker", Eric Raymond descreve o Lisp como algo parecido com latim ou grego — uma linguagem que você deve aprender como um exercício intelectual, mesmo que você não a use realmente:

Lisp vale a pena aprender pela profunda experiência de iluminação que você terá quando finalmente entender; essa experiência fará de você um programador melhor pelo resto de seus dias, mesmo que você nunca realmente use muito o Lisp em si.

Se eu não soubesse Lisp, ler isso me faria fazer perguntas. Uma linguagem que me tornaria um programador melhor, se isso significar algo, significa uma linguagem que seria melhor para programar. E essa é, de fato, a implicação do que Eric está dizendo.

Enquanto essa ideia ainda estiver circulando, acho que os hackers serão receptivos a um novo Lisp, mesmo que seja chamado de Lisp. Mas esse Lisp deve ser uma linguagem de hacker, como os LISP clássicos da década de 1970. Deve ser conciso, simples e hackeável. E deve ter bibliotecas poderosas para fazer o que os hackers querem fazer agora.

No que diz respeito às bibliotecas, acho que há espaço para superar linguagens como Perl e Python em seu próprio jogo. Muitas das novas aplicações que precisarão ser escritas nos próximos anos serão aplicações baseadas em servidor. Não há razão para que um novo Lisp não tenha bibliotecas de string tão boas quanto as do Perl, e se esse novo Lisp também tivesse bibliotecas poderosas para aplicações baseadas em servidor, poderia ser muito popular. Hackers reais não vão torcer o nariz para uma nova ferramenta que os permita resolver problemas difíceis com algumas chamadas de biblioteca. Lembre-se, hackers são preguiçosos.

Poderia ser uma vitória ainda maior ter suporte de linguagem central para aplicações baseadas em servidor. Por exemplo, suporte explícito para programas com múltiplos usuários, ou propriedade de dados no nível de tags de tipo.

Aplicações baseadas em servidor também nos dão a resposta para a pergunta de como esse novo Lisp será usado para hackear. Não faria mal tornar o Lisp melhor como uma linguagem de script para Unix. (Seria difícil torná-lo pior.) Mas acho que há áreas onde linguagens existentes seriam mais fáceis de superar. Acho que pode ser melhor seguir o modelo do Tcl e fornecer o Lisp junto com um sistema completo para suportar aplicações baseadas em servidor. O Lisp é uma combinação natural para aplicações baseadas em servidor. Fechamentos lexicais fornecem uma maneira de obter o efeito de sub-rotinas quando a interface do usuário é apenas uma série de páginas da web. S-expressões se encaixam bem no html, e macros são boas em gerá-las. Precisam haver melhores ferramentas para escrever aplicações baseadas em servidor, e precisa haver um novo Lisp, e os dois funcionariam muito bem juntos.

12 A Linguagem dos Sonhos

Como um resumo, vamos tentar descrever a linguagem dos sonhos do hacker. A linguagem dos sonhos é bonita, limpa e concisa. Tem um nível interativo que inicia rapidamente. Você pode escrever programas para resolver problemas comuns com muito pouco código. Quase todo o código em qualquer programa que você escreve é código específico para sua aplicação. Todo o resto já foi feito para você.

A sintaxe da linguagem é breve a ponto de ser um defeito. Você nunca precisa digitar um caractere desnecessário, ou mesmo usar a tecla shift muito.

Usando grandes abstrações, você pode escrever a primeira versão de um programa muito rapidamente. Mais tarde, quando você quiser otimizar, há um profiler realmente bom que lhe diz onde concentrar sua atenção. Você pode tornar loops internos extremamente rápidos, até mesmo escrevendo bytecode inline se precisar.

Há muitos bons exemplos para aprender, e a linguagem é intuitiva o suficiente para que você possa aprender a usá-la a partir de exemplos em alguns minutos. Você não precisa olhar muito no manual. O manual é fino e tem poucos avisos e qualificações.

A linguagem tem um núcleo pequeno e bibliotecas poderosas e altamente ortogonais que são tão cuidadosamente projetadas quanto a linguagem central. As bibliotecas funcionam bem juntas; tudo na linguagem se encaixa como as partes de uma boa câmera. Nada é depreciado ou mantido por compatibilidade. O código-fonte de todas as bibliotecas está prontamente disponível. É fácil se comunicar com o sistema operacional e com aplicações escritas em outras linguagens.

A linguagem é construída em camadas. As abstrações de nível superior são construídas de uma maneira muito transparente a partir de abstrações de nível inferior, que você pode acessar se quiser.

Nada é escondido de você que não precise ser. A linguagem oferece abstrações apenas como uma forma de economizar seu trabalho, em vez de como uma forma de dizer o que fazer. De fato, a linguagem incentiva você a ser um participante igual em seu design. Você pode mudar tudo sobre ela, incluindo até mesmo sua sintaxe, e qualquer coisa que você escrever tem, tanto quanto possível, o mesmo status do que vem pré-definido.

Notas

[1] Macros muito próximas da ideia moderna foram propostas por Timothy Hart em 1964, dois anos após o lançamento do Lisp 1.5. O que estava faltando, inicialmente, eram maneiras de evitar captura de variáveis e múltiplas avaliações; os exemplos de Hart estão sujeitos a ambas.

[2] Em Quando o Ar Ataca Seu Cérebro, o neurocirurgião Frank Vertosick relata uma conversa em que seu residente chefe, Gary, fala sobre a diferença entre cirurgiões e internistas ("pulgas"):

Gary e eu pedimos uma grande pizza e encontramos uma cabine aberta. O chefe acendeu um cigarro. "Olhe para aquelas malditas pulgas, tagarelando sobre alguma doença que verão uma vez em suas vidas. Esse é o problema com as pulgas, elas só gostam das coisas bizarras. Elas odeiam seus casos de pão e manteiga. Essa é a diferença entre nós e as malditas pulgas. Veja, nós amamos grandes hérnias de disco lombar suculentas, mas elas odeiam hipertensão...."

É difícil pensar em uma hérnia de disco lombar como suculenta (exceto literalmente). E ainda assim, acho que sei o que eles querem dizer. Muitas vezes tive um bug suculento para rastrear. Alguém que não é programador acharia difícil imaginar que poderia haver prazer em um bug. Certamente é melhor se tudo simplesmente funcionar. De certa forma, é. E ainda assim, há inegavelmente uma satisfação sombria em caçar certos tipos de bugs.