Linguagens de ProgramaçãoParadigmas de Linguagens de Programação
Classificação de Linguagens de Programação
12 min de leitura · 9ª mais cobrada em Linguagens de Programação
O que é isto
Classificar uma linguagem é dizer qual estilo de programa ela favorece: o que o programador escreve e o que conta como execução. O mesmo problema — ordenar uma lista, consultar um parentesco — muda de forma quando a linguagem é imperativa (o if e o while escolhem o próximo comando; a atribuição reescreve a memória), funcional (no LISP clássico o programa é uma expressão, quase sempre uma lista, que o interpretador reduz), orientada a objetos (o programa se organiza em objetos), lógica (fatos e regras; LISP não é deste paradigma) ou declarativa (o resultado desejado, sem o passo a passo na memória).
A classificação não é um rótulo único. Uma linguagem real mistura traços (C++ tem objetos e atribuição; LISP clássico tem setq). Ainda assim, cada paradigma — cada um desses estilos — tem um núcleo: um traço que, se desaparecer, a linguagem deixa de ser daquela família. Classificar serve para prever esse núcleo. Se o nome é LISP, o esperado é função e lista. Se o nome é C, o esperado é comando e memória.
Alto e baixo nível medem outra coisa: quão perto a notação está da máquina. Assembly nomeia registrador e endereço — baixo nível. Python nomeia lista e dicionário — alto nível. C fica no meio. Esse eixo não é paradigma: assembly e Python podem ser ambos imperativos; o que muda é o tamanho do passo que o programador escreve. Compilado versus interpretado — o texto vira instrução de máquina de uma vez ou comando a comando — também não escolhe a família.
O mapa é traço paradigma linguagem-emblema. O mesmo mapa cobre ainda genealogia (uma linguagem que estende outra) e geração (1GL a 5GL) — eixos que não escolhem o paradigma.
Ideia central
Um paradigma junta primitivas e uma noção de programa correto. Cinco núcleos:
| Paradigma | O que o programador descreve | Traço definidor |
|---|---|---|
| Imperativo (procedural) | comandos em sequência | atribuição que altera o estado da memória |
| Funcional | funções matemáticas | imutabilidade (valores que não se reescrevem) e ausência de efeito colateral (a avaliação não altera memória nem o mundo) |
| Orientado a objetos | entidades com dados e comportamento | objeto, classe, herança (relação é-um) |
| Lógico | fatos e regras | inferência automática (unificação: casar termos e ligar variáveis) |
| Declarativo | o resultado desejado | o quê, sem detalhar o como |
O declarativo é o guarda-chuva. Funcional e lógico são declarativos: o programador descreve o resultado, sem enumerar cada escrita na memória. O lógico é o caso em que o "quê" vem como cláusulas e o motor encadeia regras. Uma linguagem de regras de produção (CLIPS) — se a condição cabe, o motor dispara a ação — também é declarativa, sem ser Prolog.
Se o texto diz só "descreve o resultado, sem a sequência", o rótulo é declarativo. Só quando cita fatos, regras e inferência o rótulo aperta para lógico. Funcional também é declarativo (o valor, sem estado de memória explícito), mas o traço que o distingue é função + imutabilidade, não cláusula. Dois rótulos não ocupam a mesma frase: a mais larga e a mais estreita não são intercambiáveis.
Há ainda o estilo de tipos abstratos de dados: o programa se estrutura em tipos com operação pública e representação oculta. Não é um sexto núcleo da tabela acima; é o que a linguagem CLU tornou primitivo (clusters). CLU organiza tipos abstratos: o cluster esconde a representação interna do tipo; o objeto Smalltalk é a instância que recebe a mensagem. Os dois encapsulam, mas só o segundo traz é-um e envio.
Dado um traço ("comandos sequenciais alteram memória") ou um nome clássico (C, LISP, Smalltalk, CLU, CLIPS), recupera-se o paradigma e recusa-se o casamento errado. LISP não é o exemplo de orientação a objetos. C não é o de inferência. Smalltalk não é o de "só funções puras".
O núcleo é o que prevê o programa. Se a frase fala em atribuição que reescreve célula, o desenho é uma sequência de estados da memória — mesmo que a linguagem também tenha funções. Se a frase fala em unificar parent(john, X) com um fato, o desenho é inferência — mesmo que o arquivo se chame "regras". Mistura existe; o mapa aponta o traço cuja ausência faz a família deixar de se reconhecer.
Tipagem estática vs. dinâmica é quando os tipos são checados, não um paradigma. Uma linguagem funcional pode ser estática (Haskell) ou dinâmica (LISP clássico). C é estático e imperativo. Python é dinâmico e, no núcleo usual, imperativo. Não use "linguagem dinâmica" como sinônimo de família, nem "estática" como sinônimo de C.
Exemplo
Uma frase descreve o estilo; o nome do paradigma segue. Cada item abaixo traz um traço e o rótulo correspondente.
- "Programas são sequências de atribuições que mudam variáveis." → imperativo (há estado na memória; o próximo comando o encontra escrito).
- "O valor de
f(3)não depende de chamadas anteriores." → funcional (substituição; sem célula escondida). - "Cada valor vive dentro de um objeto que responde a mensagens." → orientado a objetos (instância + mensagem; cluster é outro estilo).
- "Há fatos
parent(a,b)e regras; uma consulta devolve as ligações." → lógico (unificação, além de só descrever o resultado). - "Descreve-se o resultado; o motor escolhe a ordem dos passos." → declarativo (guarda-chuva; ainda sem cláusula).
A última frase é mais larga que a do lógico. Quando a frase trata só do quê do problema, ainda não há fato, regra nem unificação: ela permanece no guarda-chuva declarativo, sem entrar no subcaso lógico. Se a frase não fala em cláusula, não marque lógico.
Dois recuos úteis. "Procedural" é o imperativo organizado em procedimentos — bloco nomeado de comandos — C, Pascal. "Tipos abstratos" aponta CLU, não Smalltalk. Se a frase fala em cluster e representação oculta sem mensagem nem herança, o rótulo é tipos abstratos (CLU).
Exemplo: casar paradigma, traço e linguagem
Cinco famílias e cinco linguagens-emblema. O casamento segue o núcleo da família, independente da data de criação ou da popularidade atual.
| Traço (o que o texto afirma) | Paradigma |
|---|---|
| Modificação explícita do estado da memória por comandos e atribuições | imperativo / procedural |
| Aplicação de funções, imutabilidade, sem efeito colateral | funcional |
| Entidades que encapsulam dados e comportamentos; herança e polimorfismo (o mesmo nome de operação vale para tipos diferentes) | orientado a objetos |
| Encadeamento de fatos e regras com inferência automática | lógico |
| Descrever o resultado sem a sequência de passos | declarativo |
Vá traço a traço. Uma frase que cita só o quê do problema é declarativa: lógico exige fatos, regras e motor de inferência. Uma consulta SQL também descreve o resultado sem ser cláusula. Se o texto não menciona regra nem fato, não marque lógico. O mesmo texto também não é funcional: não fala em função nem em imutabilidade. Cada frase carrega um núcleo; o extra que o texto não deu não se inventa.
| Linguagem | Núcleo histórico | Paradigma |
|---|---|---|
| C | comandos, atribuição, procedimentos que escrevem memória | procedural / imperativo |
| LISP | funções sobre listas; o programa é dado | funcional |
| Smalltalk | tudo é objeto; computação = envio de mensagem | orientado a objetos |
| CLU | clusters: tipo abstrato com interface e representação | tipos abstratos de dados |
| CLIPS | regras de produção; o motor dispara o que cabe | declarativo |
LISP não "usa o paradigma lógico": fatos e unificação são de Prolog. CLIPS fica no mapa declarativo; fato + consulta ?- com unificação é o núcleo lógico. Smalltalk não é CLU: mensagem e herança cluster. C não é "a linguagem de ADT": struct (agrupamento de campos) existe, mas o paradigma do C é o procedimento que altera memória.
Dado o nome, pergunte o que some se o núcleo sumir. Sem atribuição que escreve memória, C deixa de ser C. Sem função sobre lista, LISP deixa de ser o LISP clássico. Sem mensagem a objeto, Smalltalk deixa de ser Smalltalk. Sem cluster, CLU deixa de ser o emblema de tipo abstrato. Sem regra de produção disparada pelo motor, CLIPS deixa de ser o emblema declarativo.
Invertendo LISP Smalltalk, o núcleo quebra: LISP não é o exemplo de mensagem a objeto. Invertendo C CLU, C não encapsula um tipo como cluster da CLU. Invertendo CLIPS LISP, regras de produção não são defun sobre listas.
Cinco frases, lidas uma a uma, sem reaproveitar o mesmo rótulo para duas delas:
- funções matemáticas, imutabilidade, sem efeito → funcional;
- o quê do problema, sem a sequência → declarativo;
- regras e fatos com inferência → lógico;
- comandos sequenciais e atribuições → imperativo;
- entidades com dados e comportamentos, herança → orientado a objetos.
Não confunda a segunda com a terceira. A segunda não cita cláusula; a terceira cita encadeamento. Marcar as duas como "declarativo" perde o subcaso lógico. Marcar a segunda como lógico inventa fatos que o texto não deu.
O casamento C procedural não decide se uma linguagem estende outra nem em que geração ela cai: esses dois eixos vêm a seguir.
C++ como extensão de C
C++ surgiu na década de 1980 como C com Classes: a linguagem C acrescida de classe como construto. Estender, aqui, é conservar o núcleo procedural do C — comando, struct, ponteiro (variável que guarda um endereço), função — e acrescentar classe. A padronização manteve compatibilidade quase total com C: um programa C válido é, salvo cantos (palavras-chave novas como class e new), um programa C++ válido.
A compatibilidade não é idêntica: C++ reserva identificadores que em C eram nomes livres; alguns usos legais em C deixam de ser. O desenho, porém, é o de um superconjunto: o arquivo que só usa o C clássico compila como C++. Java não entra nessa genealogia — não nasceu como extensão de C e não promete que um programa C compile como Java.
O procedimento: dado o par (C, C++), pergunte se C++ foi desenhado sobre C e se um programa C típico permanece legal em C++. Se as duas respostas são sim, C++ estende C. Ter objetos (já notado no começo) não cancela a extensão: mistura de traços e genealogia são eixos distintos.
Exemplo: um soma de C em C++.
int soma(int a, int b) {
return a + b;
}
Dois parâmetros int, um return. Sem classe e sem palavra-chave que C++ tenha reservado. É C clássico.
Tipos, função e return são legais em C++. Compatibilidade: o arquivo passa nos dois lados.
Em C++ pode-se definir class Ponto { int x; int y; }; no mesmo arquivo. C não tem class. A extensão é o núcleo C mais a classe. Objetos são o acréscimo; a base procedural do C permanece.
Gerações de linguagens (1GL a 5GL)
Geração numera o eixo alto/baixo nível do começo da página: quão perto a notação está da máquina. Não é paradigma. Java orientada a objetos continua 3GL; Prolog lógico é o caso típico de 5GL. A escala canônica vai de 1GL a 5GL:
| Geração | O que o programador escreve | Exemplos |
|---|---|---|
| 1GL | bits da máquina | binário nativo |
| 2GL | mnemônicos de montagem | assembly |
| 3GL | alto nível, ainda algorítmico (if, laço, procedimento, classe) |
Fortran, Pascal, C, C++, Java |
| 4GL | notação de domínio ou consulta | SQL, geradores de relatório |
| 5GL | restrições e lógica; o motor escolhe o como | Prolog |
Exemplo: três nomes na 3GL, três nomes que não são 6GL.
Cada um escreve algoritmo de alto nível. Fortran nasceu nos anos 1950 para fórmulas e laços. Pascal descreve tipos e procedimentos. Java descreve como o método corre, com classe, if e while — não uma consulta 4GL nem um conjunto de cláusulas 5GL. Orientação a objetos é paradigma, não número de geração. Os três são 3GL.
SELECT nome FROM aluno WHERE nota > 7 descreve a consulta, sem o for que Fortran escreveria: o motor escolhe o plano — 4GL. ?- ancestral(X, joao). põe fatos, regras e inferência, sem o if da 3GL — 5GL. Fortran e Java, no passo 1, ainda descrevem como o método corre; não cabem nessas duas colunas.
As três têm variável, função, classe e laço — o mesmo texto de 3GL do passo 1. Não há uma sexta geração canônica; rotulá-las 6GL inventa um balde. Continuam 3GL de script.
Recusar o casamento errado
Três casamentos errados quebram o núcleo.
- LISP como OO ou como lógico. LISP avalia funções sobre listas. Não é Smalltalk (mensagem a objeto) nem Prolog (fato +
?-). "LISP usa o paradigma funcional." - C como ADT ou como OO. C tem
structe procedimentos. O núcleo é memória + atribuição. CLU é que tornou o cluster primitivo; Smalltalk é que tornou a mensagem primitiva. - CLIPS como lógico estreito. CLIPS é declarativo por regras de produção. O traço "fatos + unificação + consulta
?-" é o lógico (Prolog). No mapa de linguagens, CLIPS declarativo; no mapa de traços, inferência por cláusulas lógico.
Procedural imperativo: C é os dois. Declarativo lógico e (em geral) funcional: "o quê, sem a sequência" é mais largo que "regras e fatos". Se a frase não menciona regra nem fato, não marque lógico.
Alto nível não "é" funcional: Python alto nível continua podendo atribuir. Baixo nível não "é" imperativo por definição — só costuma ser, porque a máquina de von Neumann executa lendo e escrevendo memória. O mapa desta página não se lê nessa diagonal.
Em Resumo
Classificar uma linguagem é apontar o núcleo do paradigma: o que se escreve e o que conta como execução. Genealogia e geração são eixos à parte.
Imperativo = estado + atribuição; funcional = funções sem efeito; OO = objeto/classe/herança; lógico = fatos + regras + inferência; declarativo = o quê, sem detalhar o como.
Lógico e funcional são casos de declarativo. CLIPS é declarativo por regras; Prolog é o lógico clássico — e o emblema típico de 5GL.
Linguagens-emblema: C (procedural), LISP (funcional), Smalltalk (OO), CLU (tipos abstratos), CLIPS (declarativo). C++ surgiu como C com Classes e estende C, com compatibilidade quase total.
1GL = bits da máquina; 2GL = assembly; 3GL = alto nível algorítmico (Fortran, Pascal, C, C++, Java); 4GL = domínio/consultas; 5GL = restrições/lógica. PHP, Perl e Ruby não são 6GL: continuam 3GL de script.
Alto/baixo nível (a escala de gerações) e estático/dinâmico cortam outras dimensões; não escolhem o paradigma.