• Nenhum resultado encontrado

ENIAC 1943-1946 Harvard Mark

7 A revolução do hardware e do software

7.2 O desenvolvimento das linguagens

Várias linguagens, muitas delas conceitualmente diferentes entre si, foram surgindo e sendo aprimoradas, incorporando-se umas em outras. Com algumas poucas exceções, o projeto de cada linguagem foi influenciado pela experiência em linguagens anteriores. Merecem atenção especial, pelo seu pioneirismo e pelos novos paradigmas que introduziram, as linguagens alto nível FORTRAN e LISP.

* As instruções do programa em linguagem de máquina seriam convertidas em conjuntos dessas microinstruções, que então são executadas.

Com relação ao FORTRAN, em 1954 realizou-se um simpósio sobre ‘computação automática’*, e seu maior evento foi a apresentação do compilador algébrico de Laning e

Zierler (ver Os primeiros ‘compiladores’). Foi o primeiro ‘software’ que permitiu como entrada de dados um código algébrico elegante, embora limitado. Nesse meio tempo John Backus já montara um grupo de pesquisa dentro da IBM para trabalhar em um projeto sobre programação automática, a fim de responder a uma questão fundamental: “(...) pode uma máquina traduzir uma linguagem matemática abrangente em um conjunto razoável de instruções, a um baixo custo, e resolver totalmente uma questão?” [Wex80]. Em novembro de 1954 a equipe de Backus tinha criado o IBM Mathematical FORmula TRANslation System, o

FORTRAN. O primeiro parágrafo da apresentação desse trabalho enfatizava que os sistemas

anteriores ofereciam duas escolhas: ou uma fácil codificação e uma execução lenta do programa ou uma laboriosa codificação com rápida execução, mas “o FORTRAN propiciava o melhor das duas opções” [KP80]. Com o FORTRAN apareceram as expressões simbólicas, subprogramas com parâmetros, mas principalmente ocorreu a primeira tentativa de se definir rigorosamente a sintaxe de uma linguagem de programação. Um pouco mais tarde surgiu a notação BNF para a descrição sintática de uma linguagem de programação.

A história do LISP remonta a Turing e Church. Pela análise de Turing nos anos de 1936 e 1937, após seu famoso artigo sobre o décimo problema de Hilbert, o cálculo-lambda de Church, apesar da sua sintaxe simples, era suficientemente poderoso para descrever todas as funções mecanicamente computáveis, ou seja, pode ser visto paradigmaticamente como uma linguagem de programação. No cálculo-lambda, muitos problemas de programação, especificamente aqueles referentes às chamadas de procedimento, estão em sua forma mais pura, e isto influenciará diretamente linguagens como LISP e Algol [Bar84].

Em 1955 e 1956 E.K. Blum, no U.S. Naval Ordinance Laboratory desenvolveu uma linguagem completamente diferente das demais, ADES (Automatic Digital Encoding System), baseada na teoria das funções recursivas e no esquema desenvolvido para elas por Kleene. Foi a primeira linguagem “declarativa”, no sentido de que o programador estabelece as relações entre as variáveis quantitativas sem explicitamente especificar a ordem de avaliação (mais à frente se falará sobre este paradigma de programação).

Aparece agora a figura de John McCarthy, matemático, um dos primeiros a trabalhar no tema de Inteligência Artificial. Juntamente com Marvin Minsky iniciou um grande projeto nessa área. Estava procurando desenvolver uma linguagem algébrica para processamento de listas, preocupado com o problema de como representar informações da realidade por meio de sentenças escritas em uma linguagem formal adequada, e de como criar um programa que executasse fazendo inferências lógicas. Surgiu então o LISP, uma linguagem que pode ser utilizada como um formalismo para descrição de algoritmos, para escrever programas e provar propriedades de algoritmos, sendo adequada à computação simbólica e à inteligência artificial. Sobretudo com LISP pode-se visualizar melhor um importante conceito na computação moderna que é o uso de estruturas de dados como objetos abstratos. É um dos

* Aparece aqui novamente este termo, utilizado por Knuth [KP80] e John Backus [Wex80] e, de acordo com este último significava naqueles primeiros tempos “para muitos simplesmente escrever códigos mnemônicos e endereço simbólico, para outros o simples processo de acessar subrotinas de uma biblioteca e inserir nelas os endereços dos operandos. A maior parte dos sistemas de ‘programação automática’ eram programas de montagem ou conjuntos de subrotinas ou os sistemas interpretativos (...)”[Wex80].

aspectos centrais dessa linguagem, comparada a como a Matemática usa os números naturais como entidades abstratas.

Nos inícios da década de 1960, fruto do trabalho de americanos e europeus, surgiu uma linguagem projetada para representar algoritmos ao invés de se escrever programas simplesmente, o Algol-60. Ela implementava o conceito de estrutura de blocos, onde variáveis, procedimentos, etc., poderiam ser declarados onde quer que o programa os necessitasse. Algol-60 influenciou profundamente muitas linguagens que vieram depois e evoluiu para o Algol-68.

PL/I surgiu como uma tentativa de se projetar uma linguagem de uso geral

reunindo características de linguagens para aplicações numéricas como FORTRAN e Algol e para processamento de dados comerciais. Ela inovou ao permitir a construção de código de ‘baixo nível’ para o controle de exceções e o conceito de processamento concorrente, entre outros. O resultado foi algo anômalo, complexo e incoerente, de difícil implementação.

Foi a linguagem Pascal entretanto que se tornou a mais popular das linguagens do estilo Algol, por ser simples, sistemática e facilmente implementável nos diferentes computadores. O Pascal, junto com o Algol-68, está entre as primeiras linguagens com uma ampla gama de instruções para controle de fluxo, definição e construção de novos tipos de dados. Ada, que veio depois do Pascal, introduziu o conceito de pacotes e permite a construção de grandes programas com estrutura modular.

Podem-se discernir na história das linguagens certas tendências. A primeira foi a de perseguir altos níveis de abstração. Os rótulos simbólicos e mnemônicos das linguagens de montagem abstraem códigos de operação e endereços. Variáveis e atribuição abstraem acesso a um endereço de memória e atualização. Estruturas de dados abstraem formas de armazenamento. Estruturas de controle abstraem desvios. Procedimentos abstraem subrotinas. E assim por diante.

Outra tendência foi a proliferação dos paradigmas. A maioria das linguagens mencionadas até agora são imperativas, caracterizadas por comandos que atualizam variáveis. A estrutura das linguagens imperativas é induzida pelo hardware, com preocupação de que os dados trafeguem o mais rapidamente possível. Daí alguns de seus aspectos relevantes: seqüência de comandos, atribuição, controles (loopings), etc. É ainda o paradigma dominante.

Já as linguagens que seguem o paradigma funcional (também conhecidas como

declarativas), como o LISP, tem como características a clareza e a busca de um maior poder

expressivo, procurando manter a maior independência possível do paradigma de von Neumann, que caracteriza as linguagens imperativas*. Buscam uma transparência referencial e a não

ocorrência de efeitos colaterais nas suas instruções. Em LISP não há o conceito de estado – dado por uma atribuição –, memória, seqüência de instruções, etc., procurando-se tornar mais visível o uso das funções. Nas linguagens imperativas as funções dependem de estados internos, fora de seu contexto ( x := x + ‘argumento’), com a produção de efeitos colaterais (alteração de valores, impressão, etc.). LISP foi a ancestral das linguagens funcionais que

* O paradigma ou arquitetura de von Neumann refere-se ao conceito de programa armazenado, conforme documento apresentado por Neumann em junho de 1945 sobre o EDVAC. As linguagens imperativas, preocupadas com performance de execução, têm em em conta o trânsito dos dados entre a unidade central de processamento e os dispositivos onde estão armazenadas instruções e informações .

culminaram atualmente em linguagens como Miranda, ML e Haskell, que tratam funções como valores de primeira classe.

Figura 39: Gargalo de von Neumann

Smalltalk é uma linguagem baseada em classes de objetos. Um objeto é uma variável

que pode ser acessada somente através de operações associadas a ele. Smalltalk é um exemplo de uma linguagem que segue o paradigma de orientação a objeto. Simula foi um ancestral de tais linguagens.

É importante reparar que a notação matemática em sua generalidade não é facilmente implementável. No entanto muitos projetistas de linguagens quiseram explorar subconjuntos da notação matemática em linguagens de programação. Surgiram então tentativas de se construir uma ‘linguagem lógica’, isto é, baseada em um subconjunto da lógica matemática. O computador é programado para inferir relacionamentos entre valores, ao invés de computar valores de saída a partir de valores de entrada. Prolog popularizou a linguagem lógica. Em sua forma pura é fraca e ineficiente, tendo sido alterada para incluir características não lógicas e tornar-se mais amigável como linguagem de programação.

No início da década de 1990 ocorreu uma difusão intensa do paradigma da orientação

a objeto*. Este paradigma esteve em gestação por cerca de 30 anos e as novas tecnologias como

a Internet, as necessidades geradas pela novas arquiteturas, tais como a de cliente-servidor† e a

do processamento distribuído, coincidiam com o paradigma da orientação a objeto: encapsulamento, mensagem, etc. O crescimento da Internet e o “comércio eletrônico” introduziram novas dimensões de complexidade no processo de desenvolvimento de programas. Começaram a surgir linguagens que buscam superar esses novos desafios de desenvolvimento de aplicações em um contexto heterogêneo ( arquiteturas de hardware incompatíveis, sistemas operacionais incompatíveis, plataformas operando com uma ou mais interfaces gráficas incompatíveis, etc.). Apareceram C++ e linguagens como Eifell, Objective C,

Cedar/Mesa (elaborada pela Xerox, para fazer pesquisa de dados), Delphi (uma evolução da

linguagem Pascal) entre outras. E, “o próximo passo ou um paradigma completamente novo” [GM95], surge a linguagem JAVA.

* Falando de uma maneira mais técnica e bastante genérica, significa que o foco da atenção do programador recai mais nos dados da aplicação e nos métodos para manipulá-los do que nos estritos procedimentos.

Em termos gerais significa o partilhamento de uma aplicação em duas. A interface do usuário e a maioria dos programas é executada no cliente, o qual será provavelmente uma estação de trabalho ou um PC de alta performance. Os dados da aplicação residem no servidor, provavelmente em um banco de dados de um computador de grande porte. Desta maneira mantêm-se os dados onde podem ser melhor protegidos, atualizados, salvos, enquanto que o poder computacional fica distribuído diretamente pelas mesas de trabalho dos ‘clientes’.

A origem da Java está ligada a um grupo de pesquisa e desenvolvimento da Sun Microsystems formado em 1990, liderado por Patrick Naughton e James Gosling, que buscava uma nova ferramenta de comunicação e programação independente da arquitetura de qualquer dispositivo eletrônico. Em 1994, após o surgimento do NCSA Mosaic e a popularização da Internet, a equipe redirecionou os seus esforços a fim de criar uma linguagem para aplicações multimídia on line.

Conforme Linden [Lin96], Java foi inspirada por várias linguagens: tem a concorrência da Mesa, tratamento de exceções como Modula-3, linking dinâmico de código novo e gerenciamento automático de memória como LISP, definição de interfaces como Objective C, e declarações ordinárias como C. Apesar dessas qualidades, todas importantes, na verdade duas outras realmente fazem a diferença e tornam Java extremamente atrativa: sua

portabilidade e o novo conceito de arquitetura neutra.

Portabilidade significa que Java foi projetada objetivando aplicações para vários sistemas heterogêneos que podem compor uma rede como a Internet, por exemplo, e as diferentes características dessa rede. Java procura obter os mesmos resultados de processamento nas diferentes plataformas.

Por arquitetura neutra entende-se que programas em Java são compilados para se obter um código objeto (byte code na terminologia Java) que poderá ser executado em um Power PC que use o sistema operacional OS/2, ou em um sistema baseado no chip Pentium debaixo do Windows 95 ou em um Macintosh usando MacOs, ou em uma estação de trabalho Sparc rodando Unix. Ou seja, em qualquer computador, desde que tal computador implemente o ambiente necessário para isso, denominado conceitualmente de Máquina Virtual

Java.

Com a linguagem Java se começou a superar barreira que impedia que a Internet se tornasse um computador: a barreira que impedia o uso de um software utilizado em um determinado lugar, executando-o em qualquer plataforma.