Mais sobre a evolução de linguagens de programação

May 14th, 2003 § 6 comments

Há quase dois meses atrás, eu escrevi uma entrada neste blog sobre a evolução de linguagens de programação. No texto eu notei que muitas das linguagens mais difundidas atualmente são apenas variações sintáticas de linguagens mais antigas que, conseqüentemente, não contribuem com nada de novo para o campo. Eu também perguntei sobre qual caminho evolutivo novas linguagens seguiriam visto que, obviamente, as necessidades de programação não permanecerão constantes nos próximos anos. O texto era mais ou menos uma grande questão já que eu estava apenas tentando pensar em como as linguagens de programação seriam no futuro olhando para tendências atuais como tipagem dinâmica, máquina virtuais just-in-time, programação genérica, e outros conceitos que não são necessariamente novos mais que ganharam uma aceitação maior nos últimos tempos. Eu também argumentei que muitas das mais poderosas linguagens de programação são baseadas em um núcleo relativamente simples que pode ser extendido facilmente para representar novos conceitos.

Nas semanas que se seguiram à publicação do texto, eu descobri que muitas pessoas estavam interessadas no mesmo tópico e que muitas haviam escrito independentemente sobre o assunto em seus blogs ou sites. Eu também achei links para mais material interessantes que, junto com esses textos mencionados, ajudaram a responder algumas das minhas questões e me dar uma visão interessante do futuro das linguagens de programação, validando algumas das minhas idéias no processo. Alguns desses idéias e conclusão são apresentadas abaixo.

Um dos primeiros documentos que eu achei foi uma apresentação PowerPoint por Todd Proebsting, um pesquisador da Microsoft. Nessa apresentação, entitulada Disruptive Programming Language Technologies (PowerPoint, 218KB), Proebsting argumenta que o problema mais importante na criação de uma linguagem de programação moderna é como melhorar a produtividade do programador já que o hardware hoje é suficientemente poderoso e não requer que compiladores otimizem massivamente o código e os dados de um programa para obter uma boa performance. Embora eu obviamente não estive presente à apresentação, eu posso facilmente ver o que Proebsting quer dizer. Em meu próprio texto original eu escrevi que, como a era de máquina virtuais ineficiente acabou, a experimentação com novas técnicas provavelmente aumentaria, dado que máquinas virtuais eficientes são um solo mais fértil para a criação de novos conceitos sem as restrições da otimização forçada.

O ponto principal da apresentação de Proebsting não é mostrar como novas linguagens podem ser criadas, mas como elas podem ter sucesso onde outras falharam. Entretanto, a sua visão do problema é digna de nota: Proebsting diz que algoritmos e modelagem apropriada são suficientes para o desenvolvimento de programas eficientes em compiladores modernos e que criadores de linguagens deveriam se preocupar em meios de ajudar programadores a desenvolverem bons programas rapidamente, corretamente e facilmente. Ele continua notando que linguagens de programação são o local certo para resolver o problema já que elas estão na raiz do processo, em oposição à engenharia de software e análise.

Um outro artigo que eu encontrei foi o amplamente divulgado The Hundred-Year Language, por Paul Graham. Neste artigo, baseado em uma palestra que ele deu na PyCon, Graham tenta imaginar com as linguagens de programação serão daqui a cem anos. Ele argumenta que as linguagens do futuro provavelmente serão baseadas em um núcleo limpo e conciso contendo todos os axiomas a partir dos quais as outras características da linguagem poderão ser derivadas — um ponto que eu também fiz em meu texto, embora eu não tenho sido tão claro. Ele escreve:

No mínimo, um exercício que pode ser útil é analisar cuidadosamente o núcleo de uma linguagem para verificar se há axiomas que podem ser eliminados. Em minha longa carreira como um chato eu descobri que lixo sempre gera mais lixo, e eu já vi isso acontecer tanto em software como embaixo de camas e em cantos de quartos.

Ocorre-me que os galhos principais da árvore evolutiva [da programação] passam por linguagens que tem menores e mais limpos núcleos. Quanto mais da linguagem você pode escrever nela mesma, melhor.

(Tradução livre.)

Graham é um evangelista de sua linguagem preferida: Lisp. E Lisp é, de fato um exemplo perfeito de uma linguagem concisa. Ela tem um conjunto pequeno de regras sintáticas e semânticas que pode ser extendido de maneiras quase que imagináveis para criar novas formas de expressar problemas de uma maneira muito inteligível e de fácil manutenção. Eu acredito que Graham está certo. Linguagens sintaticamente mais simples são inerentemente mais poderosas por que não requerem que os programadores mantenham um excesso de conceitos em suas mentes ao mesmo tempo, o que naturalmente leva a programas mais sucintos e com menos erros. Como Proebsting, Grahama acredita que a velocidade das máquinas não sera um problema para linguagens do futuro. Ao contrário, a velocidade será uma das coisas que ajudarão os criadores de linguagens a se concentrarem no que realmente importa: a simplificação das estruturas de dados para aumentar o poder de expressão do código. Graham explica com isso pode acontecer:

Muitas estruturas de dados somente existem por causa da necessidade de velocidade de execução. Por exemplo, muitas linguagens atuais possuem strings e listas. Semanticamente, strings são mais ou menos um subconjunto de listas em que todos os elementos são caracteres. Então por que precisamos de um tipo separado? Na verdade, não precisamos. Strings somente existem por razões de eficiência. Mas é uma idiotice bagunçar a semântica de uma linguagem com gambiarras para fazer programas rodarem mais rápido. Colocar strings em uma linguagem parece ser um caso de otimização prematura.

(Tradução livre.)

Smalltalk é um bom exemplo desse tipo de simplicação necessária em linguagens de programação. Tomando strings com um exemplo, implementações Smalltalk possuem uma classe genérica apropriadamente nomeada String para lidar com, obviamente, strings. Essa classe é uma das descendentes da super-classe CharacterArray, que é outra classe genérica lidando com conjuntos arbitrários de caracteres, com o seu nome diz. Essa classe, em seqüência, descende da super-classe Collection, que novamente é uma classe genérica que lida com coleções arbitrárias. Cada nível da hierarquia adiciona funcionalidade suficiente para lidar com o seu domínio de problema. Assim, a classe Collection possui outros descendentes com Set, Bag, e Dictionary que lidam com casos especiais de listas. Da mesma maneira, a classe String possui descendentes especiais que lidam com diferentes tipos de strings. Todos esses descendentes estão facilmente integrados na estrutura maior de classes da linguagem oferecendo funcionalidades específicas sem sujar a própria linguagem com obscuros elementos sintáticos. É interessante notar também que a introdução de programação genérica em muitas linguagens de programação é apenas um pobre tentativa de conseguir o tipo natural de polimorfismo mostrado pelo exemplo acima.

Entretanto, mais a frente no seu artigo, Graham critica a orientação a objetos, e escreve:

(…) Embora eu não acredite que [a orientação a objetos] possa oferecer algo de útil a bons programadores, exceto em certos domínios especializados, ela é irresistível em grandes organizações. A programação orientada a objetos oferece um modo sustentável de escrever código espaguete. Ele permite que uma pessoa crie programas como uma série de remendos. Grandes organizações tendem a desenvolver programas desde modo, e eu imagino que isso será verdade em cem anos como o é hoje.

(Tradução livre.)

Eu acho que Graham falha em compreender uma questão sobre linguagens de programação aqui. A maior parte dos programas é escrita em uma série de pequenos passos. Ele mesmo considera esse fato em um outro de seus artigos. O maior problema com a orientação a objetos não está no paradigma em si, mas no modo com as linguagens são implementadas e em como os ambientes de desenvolvimento são criados. Da mesma maneira que o ambiente de desenvolvimento Lisp é parte do que faz a linguagem poderosa, assim também a orientação a objetos precisa de um ambiente poderoso para se desenvolver. Basta olhar para Smalltalk como um exemplo. Nesta linguagem, os programas são realmente escritos em pequenos passos, com classes simples contruindo sobre outras classes simples e chegando finalmente a aplicações completas que são extremamente flexíveis e fáceis de serem mantidas. Entretanto, sem um suporte apropriado do ambiente, os relacionamentos entre as classes rapidamente escalariam, fugindo ao controle do programador. Smalltalk possui um ambiente poderoso que segue a mesma filosofia da linguagem mostrando ao programador um visão de alto nível das classes que formam esse ambiente e permitindo que as mesmas seja facilmente manipuladas, refatoradas e extendidas. Outras linguagens modernas de programação, com Java e C#, falham em reconhecer esse fato e usam ambientes de desenvolvimento trivias que não passam de editores de texto glorificados. Essas linguagens possuem outras falhas, de fato, mas a falta de um bom ambiente só aumenta essas deficiências.

Alguém poderia argumentar que se a linguagem fosse mais simples o ambiente não seria necessária. Entretanto, continuando em Smalltalk como um exemplo, é fácil perceber que essa linguagem é construída sobre algumas regras simples das quais a mais importante é o mecanismo de passagem de mensagens. Desse ponto de vista é possível considerar as classes como simples pacotes agrupando funcionalidade separada que passam mensagens entre si para fazer a aplicação funcionar. Isso quer dizer que Smalltalk é tão simples quando deve ser. Como Einstein disse uma vez: “As coisas devem ser feitas da maneira mais simples possível, mas não mais simples do que isso”. O ambiente é apenas uma ferramenta que torna a busca e manipulação desses pacotes mais fácil.

Finalmente, deve ser notado que Smalltalk permite a manipulação das classes principais que compõem a sua estrutura. Assim, um programador pode modificar e adicionar características a essas classes — como Boolean e Number, por exemplo — sem a necessidade de derivar novas classes das mesmas. Este é outro fato que a torna uma linguagem extremamente poderosa, e novamente é um conceito notavelmente ausente de Java, C# e outros linguagens de programação orientadas a objetos.

O problema com Smalltalk, então, não está em sua natureza orientada a objetos, mas sim nas características que lhe faltam para ser uma linguagem verdadeiramente extensível. De fato, é possível extender o compilador da mesma, mas isso está além do que a maioria dos programadores aceitaria com forma de melhorar uma linguagem.

Uma boa maneira de extender uma linguagem orientada a objetos é misturar diferente paradigmas na mesma, com Python faz. Muito do sucesso desta linguagem resulta da sua habilidade de juntar diferentes conceitos, integrando noções de programação orientada a objetos, programação funcional e programação imperativa, para criar uma linguagem legível e expressiva que mantém sua simplicidade.

Um outro documento interessante que eu encontrei foi uma proposta de palestra escrita por outros dois pesquisadores da Microsoft, Erik Meijer and Wolfram Schulte, entitulado Unifying Tables, Objects, and Documents (PDF, 163KB). O documento também focaliza a questão da produtividade do programador e propõe extensões para um linguagem estaticamente tipada (que pode ser C# ou Java) para suportar nativamente alguns tipos comuns de dados presentes nos ambientes atuais, especificamente dados relacionais (tabelas SQL) e dados hierárquicos (XML). As extensões são realmente interessantes e simplificariam consideravelmente a tarefa de manipular tais estruturas nas languages referidas — o documento apresenta sintaxe alternativa para streams, tuplas, uniões, classes de conteúdo e pesquisas. Entretanto, adicionar essas extensões ao núcleo da linguagem não resolve o problema de tornar a mesma diretamente extensível para o programador. As extensões propostas são apenas “açúcar sintático”, e uma linguagem explicitamente criada para permitir extensibilidade aceitaria tais extensões naturalmente. Isso quer dizer que programadores estariam usando as extensões naturalmente, sem a necessidade de um esforço consciente para evoluir a linguagem.

O documento me lembra de um problema que tenho enfrentado constantemente em todos os meus anos como desenvolver Web. Quando eu comecei a desenvolver esse tipo de aplicações, eu estava usando o Delphi da Borland como minha linguagem primária. Naturalmente, decidi continuar a usá-lo nas aplicações Web para aproveitar o meu conhecimento da linguagem e de suas biblioteca de classes. Naquele tempo, alguns pessoas que eu conhecia estavam usando o Visual Basic para desenvolver aplicações CGI de modo que eu obtive esse código e o portei para Delphi. Rapidamente eu descobri que, embora o código pronto fosse uma grande ajuda, a tarefa de produzir HTML e lidar com formulários continuava a ser terrivelmente entediante e manual. Assim, nos anos seguintes eu desenvolvi uma série de bibliotecas para tornar a criação de HTML mais fácil e lidar automaticamente com a submissão e validação de formulários. Passei também por três bibliotecas profissionais. Entretanto, eu nunca fiquei completamente satisfeito por que, mesmo usando mecanismos baseados em modelos genéricos, a tarefa continuava entediante, repetitiva e passível de erros. HTML, como uma linguagem estruturada simplesmente não se encaixava com a Delphi. Assim, a idéia que o documento descreve para lidar com HTML é interessante por que torna o aspecto estrutural do mesmo (ou de XML, no caso) parte da linguagem, reduzindo a necessidade de mudanças de contexto e aumentando a produtividade. Mais ainda, é muito melhor que os ambientes atuais que permitem misturar código HTML e programação por que gera uma clara separação entre lógica e apresentação. Várias linguagens procuraram resolver esse problema, mas nenhuma ainda conseguiu.

O documento de Meijer and Schulte é limitado em que ele somente contempla uma forma restrita de extensão da linguagem, que teria que ser implementada na base da mesma e não resolveria outros problemas gerais. Outra documento, a dissertação de doutorado de Donovan Kolbly, que foi mencionada na discussão do documento anterior, propõe uma nova forma de construção de linguagens arbitrariamente extensíveis baseadas na técnica de parsing Earley. Neste caso, a linguagem alvo adquiriria funcionalidade similar às macros em Lisp, tornando-a extremamente extensível.

Vale a pena notar também que há um preço a pagar pela extensibilidade. Em uma entrada em seu weblog, há cerca de dois meses atrás, Rafe Colburn mostrou um trecho de código Perl extremamente idiossincrático presente no Blosxom, uma ferramenta de gerenciamente de weblogs. O código é virtualmente incompreensível para programadores que não conheçam Perl, e mesmo para os que conhecem é de difícil compreensão. Isso acontece porque a sintaxe da linguagem permite que algoritmos sejam expressos de maneira muito mais concisa do que possível em outras linguagens, mas essa expressibilidade pode gerar código ilegível justamente por produzir trechos muito idiomáticos. Uma linguagem aberta em termos de extensibilidade sofreria do mesmo problema. Partes do código usando uma sintaxe extendida poderiam eventualmente precisar de documentação e interpretação extra para serem compreendidos. Em alguns casos, essa necessidade poderia até cancelar os benefícios da extensibilidade.

Em outro dos seus artigos, o também recentemente famoso Hackers and Painters, Paul Graham escreve:

O código fonte deve, também, explicar a si próprio. Se eu pudesse fazer com que as pessoas lembrassem de apenas uma citação sobre programação, seria aquela no começo do Structure and Interpretation of Computer Programs:

Programas devem ser escritos para pessoas lerem, e somente incidentalmente para serem executados por máquinas.

A longo prazo a extensibilidade é sempre melhor porque permite que a linguagem cresça além do seu propósito original, mas isso não quer dizer muito se a mesma se torna ininteligível.

A extensibilidade é também importante hoje no cenário da programação por causa dos novos requerimentos de um mundo interconectado com protocolos que mudam rapidamente. Claro, um programador sempre pode usar programação do mais baixo nível para implementar qualquer coisa, mas é muito melhor se os mecanismos para integração existem na própria linguagem. A implementação XML-RPC de Python é um bom exemplo disso pois permite o uso transparente de métodos remotos como se eles pertencessem à classe que implementa a conexão. Integração direta com banco de dados, manipulação de dados textuais estruturados, e outros itens similares poderiam igualmente se beneficiar de implementações similares.

O que todos estes fatos significam? Eu acho que é possível agora chegar a algumas conclusões sobre o que foi mencionado acima:

Primeiro, linguagens de programação do futuro provavelmente valorizarão mais a produtividade do que o poder bruto. À medida que hardware se torna mais barato e mais poderoso haverá mais espaço para experimentação sem as restrições impostas pela necessidade de otimização. Máquinas virtuais — e, quem sabe, processadores extendidos — permitirão que as linguagens cresçam além de suas origens baseadas em um tempo em que memória e processamento era recursos escassos.

Segundo, linguagens de programação do futuro provavelmente serão mais concisas, construindo sobre bases mais simples. Seres humanos não podem manter um excesso de conceitos em suas mentes ao mesmo tempo. Programadores não são muito diferentes por melhores que sejam. Linguagens que se encaixam perfeitamente na cérebro de um programador são muito mais eficientes porque permitem que este faça uso de todos os recursos da mesma sem precisar se lembrar de muitos conceitos diferentes ao mesmo tempo. Obviamente, cada pessoa possui seus próprios limites e diferentes linguagens atenderão diferentes limites.

Terceiro, linguagens de programação do futuro provavelmente agregarão diferentes paradigmas. Cada paradigma existente possui benefícios distintos. Não é necessário construir uma linguagem sobre um único paradigma, comp Python e outras linguagens demonstram ser possível.

Quarto, linguagens de programação do futuro provavelmente farão uso de bons ambientes de desenvolvimento para melhorar a produtividade. Eu acredito que um bom ambiente é fundamental para a produtividade. Os ambientes encontrados atualmente são, em sua maioria, pouco mais que editores de texto glorificados, e há muito espaço para melhorias. Testes automáticos e refatoração são alguns exemplos pequenos de como bons ambientes podem ajudar uma linguagem de programação.

Quinto, linguagens de programação do futuro provavelmente encontrarão modos de equilibrar concisão e legibilidade. Concisão em uma linguagem de programação é uma das chaves para maior produtividade, mas pode também degradar as possibilidades de manutenção do código. Meios terão de ser encontrados de compensar a concisão mantendo a legibilidade. E como linguagens são apenas notação, é apenas uma questão de encontrar a maneira apropriada de representar as construções da linguagens de uma maneira simples, direta e inteligível.

Finalmente, linguagens de programação do futuro provavelmente integrarão mecanismos de extensão nos níveis semânticos e sintáticos mais básicos das mesmas, permitindo que programadores as evoluam de maneira simples quando necessário. Muitas tarefas em programação podem ser enormemente simplificadas por extensões da linguagem. E, em muitos casos, essas extensões não precisam passar de um “açúcar sintático”. Tome, por exemplo, os loops for each ou for ... in ... encontrados em muitos linguagens atuais. Esses loops não passam de interfaces simplificadas para enumerações. E mais, no seu nível mais básico, todos os loops são simplesmente loops while. Prover meios para que programadores posssam esconder estruturas complexas e algoritmos atrás de uma fachada sintática é uma proderosa forma de extensibilidade. A mesma extensibilidade em termos semânticos é mais complexa, mas não impossível. Como foi dito antes, tal poder vem a um preço, mas este deve ser pago se a linguagem precisa evoluir.

Algumas observações finais:

Em todos os pontos acima eu estou falando sobre linguagens a serem criadas em um futuro próximo É perfeitamente possível que um gênio ainda desconhecido descubra um novo paradigma de programação que radicalmente mude o modo com a programação existe hoje. Obviamente, tal desenvolvimento não pode ser previso e eu nem o considerei na análise acima.

Estou interessado em ouvir análises complementares, refutações e mesmo ataques. Sinta-se livre para usar os comentários abaixo para isso.

§ 6 Responses to Mais sobre a evolução de linguagens de programação"

What's this?

You are currently reading Mais sobre a evolução de linguagens de programação at Superfície Reflexiva.

meta