SER POPULAR
OriginalMay 2001
(Este artigo foi escrito como uma espécie de plano de negócios para uma nova linguagem. Portanto, ele está faltando (porque considera como certo) o recurso mais importante de uma boa linguagem de programação: abstrações muito poderosas.)
Um amigo meu certa 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 perda de tempo, que as linguagens de programação não se tornam populares ou impopulares com base em seus méritos, e que, portanto, não importa quão boa fosse sua linguagem, ninguém a usaria. Pelo menos, foi o que aconteceu com a linguagem que ele havia projetado.
O que torna uma linguagem 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 para essas perguntas podem ser encontradas observando os hackers e aprendendo o que eles querem. Linguagens de programação são para hackers, e uma linguagem de programação é boa como linguagem de programação (em vez de, digamos, um exercício em semântica denotacional ou design de compilador) 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 qual linguagem usar por outra pessoa. E ainda assim, acho que o efeito de tais fatores externos na popularidade das linguagens de programação não é tão grande quanto se pensa às vezes. Acho que um problema maior é que a ideia de um hacker sobre uma boa linguagem de programação é diferente da de muitos designers de linguagem.
Entre os dois, a opinião do hacker é a que importa. Linguagens de programação não são teoremas. São ferramentas, projetadas para pessoas, e precisam ser projetadas para se adequar às forças e fraquezas humanas tanto quanto os sapatos precisam ser projetados para os pés humanos. Se um sapato aperta quando você o coloca, é um sapato ruim, não importa quão elegante ele 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. Não significa que seja perda de tempo tentar projetar uma boa linguagem. Hackers experientes conseguem identificar uma boa linguagem quando a veem, e eles a usarão. Hackers experientes são uma minoria minúscula, é verdade, mas essa minoria minúscula escreve todo o bom software, e sua influência é tal que o resto dos programadores tenderá a usar qualquer linguagem que eles usem. Muitas vezes, na verdade, não é apenas influência, mas comando: muitas vezes os hackers experientes são as próprias pessoas que, como seus chefes ou orientadores de faculdade, dizem aos outros programadores qual linguagem usar.
A opinião de hackers experientes 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. E a popularidade separa ainda mais as boas linguagens das ruins, porque o feedback de usuários reais sempre leva a melhorias. Veja quanta mudança qualquer linguagem popular sofreu durante sua vida. Perl e Fortran são casos extremos, mas até mesmo Lisp mudou muito. O Lisp 1.5 não tinha macros, por exemplo; essas evoluíram mais tarde, depois que hackers do MIT passaram um par de anos usando Lisp para escrever programas reais. [1]
Então, se uma linguagem precisa ser boa para ser popular, acho que uma linguagem precisa ser popular para ser boa. E ela precisa continuar popular para continuar boa. O estado da arte em linguagens de programação não fica parado. E ainda assim, os Lisps que temos hoje ainda são mais ou menos o que eles tinham no MIT no meio 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 precisam saber sobre uma linguagem antes de poderem usá-la. Como eles vão ouvir? De outros hackers. Mas tem que haver algum grupo inicial de hackers usando a linguagem para que outros possam ouvir falar dela. Eu me pergunto o quão grande esse grupo precisa ser; quantos usuários formam uma massa crítica? De cabeça, eu diria vinte. Se uma linguagem tivesse vinte usuários separados, ou seja, vinte usuários que decidiram por conta própria usá-la, eu a consideraria 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 Tróia: dar às pessoas um aplicativo que elas desejam, que acontece de ser 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 precisa ser a linguagem de script de um sistema popular. Fortran e Cobol eram as linguagens de script dos primeiros mainframes IBM. C era a linguagem de script do Unix e, mais tarde, foi Perl. Tcl é a linguagem de script do Tk. Java e Javascript pretendem ser as linguagens de script dos navegadores da 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 do C, o dialeto do Lisp do MIT, chamado MacLisp, era uma das únicas 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 esse motivo, suspeito que a maioria da programação Lisp feita hoje é feita em Emacs Lisp ou AutoLisp.
Linguagens de programação não existem em isolamento. Hackear é um verbo transitivo — hackers geralmente estão hackando algo — e na prática, as linguagens são julgadas em relação ao que elas são usadas para hackear. Então, se você quiser projetar uma linguagem popular, você precisa fornecer mais do que uma linguagem, ou precisa projetar sua linguagem para substituir a linguagem de script de algum sistema existente.
Common Lisp é impopular em parte porque é órfão. Ele originalmente veio com um sistema para hackear: a Lisp Machine. Mas Lisp Machines (junto com computadores paralelos) foram esmagados pelo crescimento do poder dos processadores de uso 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 terrivelmente 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 for 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 esta deve ser gratuita. As empresas pagarão por software, mas hackers individuais nã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 quase diria que uma linguagem precisa ter um livro publicado pela O'Reilly. Isso está se tornando o teste de importância para hackers.
Deve haver documentação online também. Na verdade, o livro pode começar como documentação online. Mas não acho que os livros físicos estejam desatualizados ainda. Seu formato é conveniente, e o de fato censura imposta pelas editoras é um filtro útil, embora imperfeito. 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 é a brevidade. Os hackers são preguiçosos, da mesma forma que os matemáticos e os arquitetos modernistas são preguiçosos: eles odeiam tudo o que é supérfluo. Não estaria muito 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 essa não é exatamente a forma como os hackers pensam, um designer de linguagem faria bem em agir como se fosse.
É um erro tentar tratar o usuário com expressões longas que se destinam a se parecer com o inglês. Cobol é notório por essa falha. 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.
Já foi dito que o Lisp deveria usar first e rest em vez de car e cdr, porque isso tornaria os programas mais fáceis de ler. Talvez pelas primeiras duas horas. Mas um hacker pode aprender rapidamente o suficiente 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 se alinham quando são chamados, como car e cdr costumam ser, em linhas sucessivas. Descobri que a forma como o código se alinha na página importa muito. Mal consigo ler código Lisp quando ele é definido em uma fonte de largura variável, e amigos dizem que isso também é verdade para outras linguagens.
A brevidade é um lugar onde as linguagens fortemente tipadas perdem. Todas as outras coisas sendo iguais, ninguém quer começar um programa com um monte de declarações. Tudo o que pode ser implícito, deve ser.
Os tokens individuais também devem ser curtos. Perl e Common Lisp ocupam pólos opostos nesta questão. Programas Perl podem ser quase cripticamente densos, enquanto os nomes dos operadores integrados do Common Lisp são comicamente longos. Os designers do Common Lisp provavelmente esperavam que os usuários tivessem editores de texto que digitariam esses nomes longos 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 a brevidade para um hacker: ser capaz de fazer o que você quer. Na história das linguagens de programação um esforço surpreendente foi feito para 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 vai precisar fazer coisas que eles nunca anteciparam, em vez de um atrapalhado que precisa ser protegido de si mesmo. O atrapalhado vai atirar em si mesmo no pé 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 para sempre para fazê-lo.
Bons programadores costumam querer fazer coisas perigosas e desagradáveis. Por desagradável, quero dizer coisas que vão atrás 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 questionar o designer original.
Deixe-se ser questionado. Quando você cria alguma ferramenta, as pessoas usam ela de maneiras que você não pretendia, e isso é especialmente verdade para uma ferramenta altamente articulada como uma linguagem de programação. Muitos hackers vão querer ajustar seu modelo semântico de uma maneira que você nunca imaginou. Eu digo, deixe-os; dê ao programador acesso a tantas coisas internas quanto possível 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 remover referências a um objeto excluído, por exemplo, ou encontrar campos que não estã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 posso chamar em qualquer struct. Só posso acessar os campos por nome, porque é isso que uma struct deve significar.
Um hacker pode querer subverter o modelo pretendido das coisas apenas uma vez ou duas vezes em um grande programa. Mas que diferença faz ser capaz de. 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 cutucar as entranhas brutas, o prazer secreto do adolescente em estourar espinhas. [2] Para os 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, o Lisp tem sido bom em deixar os hackers fazerem o que quiserem. O politicamente correto do Common Lisp é uma aberração. Os Lisps antigos deixavam você colocar as mãos em tudo. Uma boa parte desse espírito é, felizmente, preservado em macros. Que coisa maravilhosa, ser capaz de fazer transformações arbitrárias no código-fonte.
As macros clássicas são uma ferramenta de verdade para hackers - simples, poderosa e perigosa. É tão fácil entender o que elas fazem: você chama uma função nos argumentos da macro, e o que ela retorna é inserido no lugar da chamada da macro. As macros higiênicas incorporam o princípio oposto. Elas tentam protegê-lo de entender o que estão fazendo. Nunca ouvi as 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. As macros higiênicas são destinadas a me proteger da captura de variáveis, entre outras coisas, mas a captura de variáveis é exatamente o que eu quero em algumas macros.
Uma linguagem realmente boa deve ser limpa e suja: bem projetada, 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. Assim eram os Lisps antigos. A linguagem de um verdadeiro hacker sempre terá um caráter ligeiramente desbocado.
Uma boa linguagem de programação deve ter recursos que façam o tipo de pessoas que usam a frase "engenharia de software" balançar a cabeça desaprovando. Na outra ponta do continuum 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 tem que ser bom 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 do sistema, ou gerar dados de teste para uma simulação, ou converter dados de um formato para outro. O 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.
Tenho a sensação de que os melhores programas grandes começam a vida dessa forma, em vez de serem projetados grandes desde o início, como a Barragem Hoover. É assustador construir algo grande do zero. Quando as pessoas assumem um projeto que é muito grande, elas ficam sobrecarregadas. O projeto ou fica atolado, ou o resultado é estéril e de madeira: um shopping center em vez de um centro real, Brasília em vez de Roma, Ada em vez de C.
Outra maneira de obter um programa grande é começar com um descartável programa e continuar melhorando. Essa abordagem é menos assustadora, e o design do programa se beneficia da evolução. Acho que, se alguém olhasse, isso acabaria sendo a forma como a maioria dos programas grandes foi desenvolvida. E aqueles que evoluíram dessa forma provavelmente ainda são escritos na linguagem em que foram escritos pela primeira vez, porque é raro um programa ser portado, exceto por razões políticas. E assim, paradoxalmente, se você quiser criar uma linguagem que é 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 descartável programa 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 nele ficavam maiores. Foi só com o Perl 5 (se é que foi) que a linguagem se tornou adequada para escrever programas sérios, e ainda assim já era incrivelmente 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. Então a linguagem provavelmente deve já estar instalada no computador que você está usando. Não pode ser algo que você tem que instalar antes de usar. 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á havia instalado.
Estar disponível significa mais do que estar instalado, no entanto. Um linguagem interativa, com uma interface de linha de comando, é mais disponível do que uma que você tem que compilar e executar separadamente. Um linguagem de programação popular deve ser interativa e iniciar rapidamente.
Outra coisa que você quer em um programa descartável é a brevidade. Brevidade é sempre atraente para hackers, e nunca mais do que em um programa que eles esperam que saia em uma hora.
6 Bibliotecas
Claro, o máximo em brevidade é ter o programa já escrito para você e simplesmente chamá-lo. E isso nos leva ao que eu acho que será um recurso cada vez mais importante das linguagens de programação: funções de biblioteca. Perl ganha porque possui grandes bibliotecas para manipulação de strings. Essa classe de funções de biblioteca é especialmente importante para programas descartáveis, que geralmente são escritos originalmente para converter ou extrair dados. Muitos programas Perl provavelmente começam como apenas algumas chamadas de biblioteca coladas.
Acho que muitos dos avanços que acontecem nas linguagens de programação nos próximos cinquenta anos terão a ver com funções de biblioteca. Acho que as linguagens de programação do futuro terão bibliotecas que são tão cuidadosamente projetadas quanto a linguagem principal. O design de linguagem de programação não será sobre se tornar sua linguagem fortemente ou fracamente tipada, ou orientada a objetos, ou funcional, ou qualquer outra coisa, mas sobre como projetar grandes bibliotecas. O tipo de designers de linguagem que gostam de pensar sobre como projetar sistemas de tipos pode se encolher com isso. É quase como escrever aplicativos! Que pena. As linguagens são para programadores, e as 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 muito grandes, às vezes leva mais tempo para encontrar a função de que você precisa do que para escrever o código sozinho. As bibliotecas precisam ser projetadas usando um pequeno conjunto de operadores ortogonais, assim como a linguagem principal. 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 conversar com o sistema operacional. Por razões históricas, o Common Lisp tenta fingir que o SO não existe. E como você não pode conversar com o SO, é improvável que você consiga escrever um programa sério usando apenas os operadores integrados 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 alto em Lisp se o Common Lisp tivesse bibliotecas de string poderosas e bom suporte ao SO.
7 Sintaxe
Uma linguagem com a sintaxe do Lisp, ou mais precisamente, a falta de sintaxe, poderia se tornar popular? Não sei a resposta para essa pergunta. Acho que a sintaxe não é o principal motivo pelo qual o Lisp não é popular atualmente. O Common Lisp tem problemas piores do que a sintaxe desconhecida. Conheço vários programadores que se sentem confortáveis com a sintaxe de prefixo e, no entanto, usam Perl por padrão, porque ele possui bibliotecas de string poderosas e pode conversar com o sistema operacional.
Existem dois possíveis problemas com a notação de prefixo: que ela é desconhecida para os programadores e que não é densa o suficiente. A sabedoria convencional no mundo Lisp é que o primeiro problema é o real. Não tenho tanta certeza. Sim, a notação de prefixo faz programadores comuns entrarem em pânico. Mas não acho que as opiniões de programadores comuns importem. As linguagens se tornam populares ou impopulares com base no que os hackers experientes pensam delas, e acho que os hackers experientes podem lidar com a notação de prefixo. A sintaxe Perl pode ser bastante incompreensível, mas isso não impediu a popularidade do Perl. Se algo, pode ter ajudado a fomentar um culto Perl.
Um problema mais sério é a difusão da notação de prefixo. Para hackers experientes, isso realmente é um problema. Ninguém quer escrever (aref a x y) quando poderia escrever a[x,y].
Nesse caso particular, existe uma maneira de contornar o problema. Se tratarmos as estruturas de dados como se fossem funções em índices, poderíamos escrever (a x y) em vez disso, o que é ainda mais curto que a forma Perl. Truques semelhantes podem encurtar outros tipos de expressões.
Podemos nos livrar (ou tornar opcional) muitos 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. Usei Lisp durante toda a minha vida de programação e ainda não acho as expressões matemáticas de prefixo 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, ela provavelmente deve ser implementada como algum tipo de macro de leitura.
Não acho que devêssemos ser religiosamente opostos a introduzir sintaxe no Lisp, desde que ela seja traduzida de uma maneira bem compreendida para as expressões-s subjacentes. Já existe uma boa quantidade de sintaxe em Lisp. Não é necessariamente ruim introduzir mais, desde que ninguém seja obrigado 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 lispy 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, os especificadores de formato poderiam ser incluídos nela. Seria uma boa coisa se as macros pudessem gerar especificadores de formato da mesma forma que geram qualquer outro tipo de código.
Um hacker Lisp eminente me disse que sua cópia do CLTL cai aberta para a seção format. A minha também. Isso provavelmente indica espaço para melhorias. Também pode significar que os programas fazem muita E/S.
8 Eficiência
Uma boa linguagem, como todos sabem, deve gerar código rápido. Mas na prática, não acho que o 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 enganados sobre onde esses gargalos estão.
Então, na prática, a maneira de obter código rápido é ter um bom perfilador, em vez de, digamos, tornar 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 com o Lisp é que é difícil dizer 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, acho que um bom perfil faria muito para resolver o problema: você logo aprenderia o que era caro.
Parte do problema aqui é social. Os designers de linguagem gostam de escrever compiladores rápidos. É assim que eles medem sua habilidade. Eles pensam no perfilador como um complemento, no máximo. Mas na prática, um bom perfilador 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 fora de sintonia com seus usuários. Eles fazem um ótimo trabalho de resolver o problema ligeiramente errado.
Pode ser uma boa ideia ter um perfilador ativo - para enviar 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 representar de alguma forma o que está acontecendo em programas em execução. Isso seria uma grande vitória, especialmente em aplicativos baseados em servidor, onde você tem muitos programas em execução para analisar. Um perfilador ativo poderia mostrar graficamente o que está acontecendo na memória enquanto um programa está sendo executado, ou até mesmo fazer sons que indicam o que está acontecendo.
O som é uma boa dica para problemas. Em um lugar onde trabalhei, tínhamos um grande quadro de mostradores mostrando o que estava acontecendo com nossos servidores web. As mãos eram movidas por pequenos servomotores que faziam um leve barulho quando giravam. Não conseguia ver o quadro da minha mesa, mas descobri que conseguia dizer imediatamente, pelo som, quando havia um problema com um servidor.
Pode até ser possível escrever um perfilador que detectaria automaticamente algoritmos ineficientes. Não ficaria surpreso se certos padrões de acesso à memória se revelassem sinais seguros de algoritmos ruins. Se houvesse um homenzinho correndo dentro do computador executando nossos programas, ele provavelmente teria uma história tão longa e lamentosa sobre seu trabalho quanto um funcionário do governo federal. Muitas vezes tenho a sensação de que estou enviando o processador para muitas caçadas de gansos selvagens, mas nunca tive uma boa maneira de olhar para o que ele está fazendo.
Vários Lisps agora compilam para bytecode, que é então executado por um interpretador. Isso geralmente é feito para tornar a implementação mais fácil de portar, mas pode ser um recurso de linguagem útil. Pode ser uma boa ideia tornar o bytecode uma parte oficial da linguagem e permitir que os 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 surgimento de aplicativos baseados em servidor, mais e mais programas podem acabar sendo limitados por E/S. Valerá a pena tornar a E/S rápida. A linguagem pode ajudar com medidas diretas, como funções de saída simples, rápidas e formatadas, e também com mudanças estruturais profundas, como cache 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. Muitos dos aplicativos interessantes escritos no futuro próximo serão baseados em servidor, e o número de usuários por servidor é a questão crítica para qualquer pessoa que hospeda tais aplicativos. No custo de capital de um negócio que oferece um aplicativo baseado em servidor, este é o divisor.
Por anos, a eficiência não importou muito na maioria dos aplicativos de 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 aplicativos baseados em servidor. Nesse mundo, o hardware e o software serão fornecidos juntos. Para empresas que oferecem aplicativos baseados em servidor, fará uma grande diferença para o resultado final quantos usuários eles podem suportar por servidor.
Em alguns aplicativos, o processador será o fator limitante, e a velocidade de execução será a coisa mais importante a ser otimizada. 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. Bom suporte para threads permitirá que todos os usuários compartilhem um único heap. Também pode ajudar a ter objetos persistentes e/ou suporte de nível de linguagem para carregamento lento.
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. Então, a maioria dos hackers tende a esperar até que uma linguagem tenha existido por alguns anos antes mesmo de considerar usá-la.
Os inventores de coisas novas e maravilhosas costumam se surpreender ao descobrir isso, mas você precisa de tempo para transmitir qualquer mensagem às pessoas. Um amigo meu raramente faz alguma coisa da primeira vez que alguém pede a ele. Ele sabe que as pessoas às vezes pedem coisas que acabam não querendo. Para evitar perder seu tempo, ele espera até a terceira ou quarta vez que lhe pedem para fazer algo; até então, quem quer que esteja pedindo a ele pode estar bastante irritado, mas pelo menos provavelmente realmente quer o que quer que esteja pedindo.
A maioria das pessoas aprendeu a fazer um tipo semelhante de filtragem em coisas novas de que ouve falar. Eles nem começam a prestar atenção até ouvir falar de algo dez vezes. Eles são perfeitamente justificados: a maioria dos novos e quentes whatevers acabam sendo uma perda de tempo e, eventualmente, desaparecem. Ao adiar o aprendizado de VRML, evitei ter que aprendê-lo.
Então, qualquer pessoa que inventa algo novo tem que esperar continuar repentindo sua mensagem por anos antes que as pessoas comecem a entender. Escrevemos o que era, até onde sei, o primeiro aplicativo baseado em servidor web, e levou anos para convencer as pessoas de que não precisava ser baixado. Não era porque eles eram estúpidos. Eles simplesmente nos desligaram.
A boa notícia é que a simples repetição resolve o problema. Tudo o que você tem que fazer é continuar contando sua história, e eventualmente as pessoas começarão a ouvir. Não é quando as pessoas percebem que você está lá que elas prestam atenção; é quando elas percebem 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, você 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 alguma quebra.
Existem duas maneiras pelas quais a nova tecnologia é introduzida: o método de crescimento orgânico e o método do big bang. O método de crescimento orgânico é exemplificado pela clássica startup de garagem subfinanciada. Um casal de caras, trabalhando na obscuridade, desenvolve uma nova tecnologia. Eles a lançam sem marketing e inicialmente têm apenas alguns (fanaticamente dedicados) usuários. Eles continuam a melhorar a tecnologia e, enquanto isso, sua base de usuários cresce por boca a boca. Antes que percebam, eles são grandes.
A outra abordagem, o método do big bang, é exemplificada pela startup apoiada por capital de risco e fortemente comercializada. Eles correm para desenvolver um produto, lançá-lo com grande publicidade e imediatamente (eles esperam) ter uma grande base de usuários.
Geralmente, os caras da garagem invejam os caras do big bang. Os caras do big bang são suaves e confiantes e respeitados pelos VCs. Eles podem pagar o 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 sua garagem, se sentem pobres e não amados. E ainda acho que eles costumam estar errados em se sentirem pena de si mesmos. O crescimento orgânico parece gerar melhor tecnologia e fundadores mais ricos do que o método do big bang. Se você olhar para as tecnologias dominantes hoje, verá que a maioria delas cresceu organicamente.
Este padrão não se aplica apenas a empresas. Você também o vê em pesquisas patrocinadas. Multics e Common Lisp eram projetos de big bang, e Unix e MacLisp eram projetos de crescimento orgânico.
10 Redesenho
"A melhor escrita é a reescrita", escreveu E. B. White. Todo bom escritor sabe disso, e é verdade para o software também. A parte mais importante do design é o redesenho. 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 como pode ser tão difícil? com metade do seu cérebro enquanto pensa nunca vai funcionar com a outra.
O truque é perceber que não há uma contradição real 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.
As pessoas que fazem um bom trabalho costumam pensar que o que quer que estejam trabalhando não é bom. Outros veem o que eles fizeram e estão cheios de admiração, mas o criador está cheio de preocupações. Este padrão não é 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 por sua confiança de que será capaz de resolvê-lo. Na segunda fase, você olha para o que fez na fria luz da manhã e vê todas as suas falhas muito claramente. Mas, enquanto seu espírito crítico não superar sua esperança, você será capaz de olhar para seu sistema, admitindo que está incompleto, e pensar, como pode ser tão difícil chegar ao resto do caminho?, continuando assim o ciclo.
É complicado manter as duas forças equilibradas. Em jovens hackers, o otimismo predomina. Eles produzem algo, estão convencidos de que é ótimo e nunca o melhoram. Em hackers antigos, o ceticismo predomina, e eles nem ousam assumir projetos ambiciosos.
Qualquer coisa que você possa fazer para manter o ciclo de redesenho funcionando é bom. A prosa pode ser reescrita várias e várias vezes até que você fique satisfeito com ela. Mas o 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 é improvável que reclamem que seus pensamentos foram quebrados por alguma incompatibilidade recém-introduzida.
Os usuários são uma faca de dois gumes. Eles podem ajudá-lo a melhorar sua linguagem, mas também podem dissuadi-lo de melhorá-la. Portanto, 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á-lo. Além disso, como regra geral, você pode, a qualquer momento, se safar mudando mais do que pensa. Introduzir mudanças é como tirar um band-aid: a dor é uma lembrança quase assim que você a sente.
Todo mundo sabe que não é uma boa ideia ter uma linguagem projetada por um comitê. Os comitês geram um mau design. Mas acho que o pior perigo dos comitês é que eles interferem no redesenho. É 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.
Mesmo um comitê de duas pessoas atrapalha o redesenho. Isso acontece particularmente nas interfaces entre partes de software escritas por duas pessoas diferentes. Para mudar a interface, ambos precisam concordar em mudá-la ao mesmo tempo. E assim as interfaces tendem a não mudar, o que é um problema porque elas tendem a ser uma das partes mais ad hoc de qualquer sistema.
Uma solução aqui pode ser projetar sistemas de forma 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 vai tender a ser propriedade de um deles. O nível inferior de dois níveis será uma linguagem na qual o superior é escrito, caso em que o nível inferior será o proprietário da interface, ou será um escravo, no qual caso a interface pode ser ditada pelo nível superior.
11 Lisp
O que tudo isso implica é que há esperança para uma nova 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 são desligados pela estranheza da Lisp. Essa ilusão reconfortante pode ter impedido-nos de ver o verdadeiro problema com a Lisp, ou pelo menos Common Lisp, que é que ela é péssima para fazer o que os hackers querem fazer. Uma linguagem de hackers precisa de bibliotecas poderosas e algo para hackear. Common Lisp não tem nenhuma das duas. Uma linguagem de hackers é concisa e hackável. Common Lisp não é.
A boa notícia é que não é a Lisp que é ruim, mas a Common Lisp. Se nós pudermos desenvolver uma nova Lisp que seja uma linguagem real de hackers, acho que os hackers vão usá-la. Eles usarão qualquer linguagem que faça o trabalho. Tudo o que temos a fazer é garantir que essa nova Lisp faça algum trabalho importante melhor do que outras linguagens.
A história oferece algum incentivo. Com o tempo, linguagens de programação novas e sucessivas têm tirado cada vez mais recursos da Lisp. Não há mais muito o que copiar antes que a linguagem que você fez seja Lisp. A última linguagem da moda, Python, é uma Lisp diluída com sintaxe infixa e sem macros. Uma nova Lisp seria um natural passo nesta progressão.
Às vezes penso que seria um bom truque de marketing chamar de uma versão aprimorada do Python. Isso soa mais moderno do que Lisp. Para muitas pessoas, Lisp é uma linguagem de IA lenta com muitos parênteses. A biografia oficial de Fritz Kunze evita cuidadosamente mencionar o L-word. Mas meu palpite é que não devemos ter medo de chamar o novo Lisp de Lisp. 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 "How to Become a Hacker", Eric Raymond descreve Lisp como algo como latim ou grego - uma linguagem que você deve aprender como um intelectual exercício, 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 vai torná-lo um programador melhor pelo resto de seus dias, mesmo que você nunca realmente use Lisp em si muito.
Se eu não conhecesse Lisp, ler isso me faria fazer perguntas. Uma linguagem que me tornaria um programador melhor, se isso significa alguma coisa, significa uma linguagem que seria melhor para programação. E essa é de fato a implicação do que Eric está dizendo.
Enquanto essa ideia ainda estiver por aí, acho que os hackers vão ser receptivos o suficiente a uma nova Lisp, mesmo que seja chamada de Lisp. Mas essa Lisp deve ser uma linguagem de hackers, como as Lisps clássicas de a década de 1970. Deve ser concisa, simples e hacká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 vencer 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 uma nova Lisp não tenha bibliotecas de string tão boas quanto Perl, e se essa nova Lisp também tivesse bibliotecas poderosas para aplicações baseadas em servidor, ela poderia ser muito popular. Hackers de verdade não vão torcer o nariz para uma nova ferramenta que vai deixar eles resolverem 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 vários 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 para que essa nova Lisp será usada para hackear. Não faria mal tornar a Lisp melhor como uma linguagem de script para Unix. (Seria difícil piorar.) Mas acho que existem áreas onde as existentes linguagens seriam mais fáceis de vencer. Acho que seria melhor seguir o modelo do Tcl e fornecer a Lisp junto com um completo sistema para suportar aplicações baseadas em servidor. Lisp é um natural ajuste para aplicações baseadas em servidor. Os closures 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. As S-expressões se mapeam bem para html e as macros são boas em gerá-lo. É preciso haver ferramentas melhores para escrever aplicações baseadas em servidor, e é preciso haver uma nova Lisp, e as duas funcionariam muito bem juntas.
12 A Linguagem dos Sonhos
Para resumir, vamos tentar descrever a linguagem dos sonhos dos hackers. A linguagem dos sonhos é linda, limpa e concisa. Ela tem um toplevel 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ê escrever é código que é específico para o seu aplicativo. Tudo o mais 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 até 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 realmente bom perfilador que lhe diz onde focar sua atenção. Você pode tornar os loops internos incrivelmente rápidos, até mesmo escrevendo código de byte inline se você precisar.
Existem 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 consultar o manual muito. 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. O bibliotecas funcionam bem juntas; tudo na linguagem se encaixa juntas como as peças de uma câmera fina. Nada é depreciado, ou retido por compatibilidade. O código-fonte de todas as bibliotecas está prontamente disponível. É fácil conversar 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 forma muito transparente a partir de abstrações de nível inferior, que você pode obter se quiser.
Nada está escondido de você que não precise absolutamente ser. O linguagem oferece abstrações apenas como uma forma de economizar trabalho, em vez de uma forma de dizer o que fazer. Na verdade, 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, o máximo 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 depois do lançamento da Lisp 1.5. O que era faltando, inicialmente, eram maneiras de evitar a captura de variáveis e múltiplas avaliação; os exemplos de Hart estão sujeitos a ambos.
[2] Em When the Air Hits Your Brain, o neurocirurgião Frank Vertosick conta uma conversa em que seu residente-chefe, Gary, fala sobre a diferença entre cirurgiões e internistas ("pulgas"):
Gary e eu pedimos uma pizza grande e encontramos um box aberto. O chefe acendeu um cigarro. "Olha essas pulgas desgraçadas, tagarelando sobre alguma doença que eles verão uma vez na vida. Essa é o problema com as pulgas, elas só gostam das coisas bizarras. Eles odeiam seus casos de pão com manteiga. Essa é a diferença entre nós e as pulgas do caralho. Veja, nós amamos grandes e suculentas hérnias de disco lombar hérnias, mas eles odeiam hipertensão...."
É difícil pensar em uma hérnia de disco lombar como suculenta (exceto literalmente). E ainda acho que sei o que eles querem dizer. Eu costumava ter 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 há, inegavelmente, uma satisfação sombria em caçar certos tipos de bugs.