Artigo original: The Great Programming Jargon Bake-off
Escrito por: Preethi Kasireddy
Imperativo x Declarativo, Puro x Impuro, Estático x Dinâmico.
Terminologias como essas são salpicadas por várias publicações em blogs de programação, palestras em conferências, artigos e livros didáticos.
Porém, você não deve se deixar intimidar por esse vocabulário. Vamos examinar e desvendar alguns desses conceitos para que você possa entender sobre o que todos esses desenvolvedores ao seu redor estão falando.
Tipagem estática x dinâmica
Isso se trata de quando a informação de tipo é adquirida — seja no momento da compilação ou em tempo de execução.
Você pode usar essa informação de tipo para detectar erros de tipo. Um erro de tipo ocorre quando um valor não é do tipo esperado.
Verificação de tipo estática
É o processo de verificar a segurança de tipo de um programa com base na análise do código-fonte do programa. Em outras palavras, a verificação de tipo ocorre no momento da compilação, permitindo que erros de tipo sejam detectados mais cedo.
Verificação de tipo dinâmica
O processo de verificar a segurança de tipo de um programa em tempo de execução. Com a verificação de tipo dinâmica, os erros de tipo ocorrem em tempo de execução.
Tipagem forte x fraca
É importante observar que forte x fraca não tem um significado técnico sobre o qual todos universalmente concordam. Por exemplo, mesmo que Java seja tipado estaticamente, toda vez que você usa reflexão ou cast, você está adiando a verificação de tipo para a execução.
Da mesma maneira, a maioria das linguagens fortemente tipadas ainda converterá automaticamente entre inteiros e ponto flutuante. Portanto, você deve evitar usar esses termos porque chamar um sistema de tipagem "forte" ou "fraca", por si só, não diz muito.
Tipagem forte
Em uma linguagem fortemente tipada, o tipo de uma construção não muda — um int
é sempre um int
e tentar usá-lo como uma string
resultará em um erro.
Tipagem fraca
Tipagem fraco significa que o tipo de uma construção pode mudar dependendo do contexto. Por exemplo, em uma linguagem de tipagem fraca, a string "123" pode ser tratada como o número 123 se você adicionar outro número a ela.
Isso geralmente significa que o sistema de tipos pode ser subvertido (invalidando quaisquer garantias) porque você pode converter um valor de um tipo para outro.
Dados mutáveis x imutáveis
Dados imutáveis
Quando um objeto não pode ser modificado após sua criação, você pode dizer que ele é "imutável", que é outro modo de dizer "inalterável". Isso significa que você alocará um novo valor para cada alteração.
Dados mutáveis
Quando você pode modificar um objeto após sua criação, ele é "mutável". Quando você tem uma referência a um objeto mutável, por exemplo, o conteúdo do objeto pode mudar.
Funções puras x impuras
Funções puras
Uma função pura tem duas qualidades:
- Ela depende apenas da entrada fornecida — e não de nenhum estado externo que possa mudar durante sua avaliação ou entre chamadas.
- Ela não causa nenhum efeito colateral semanticamente observável, como modificar um objeto global ou um parâmetro passado por referência.
Funções impuras
Qualquer função que não atenda a esses dois requisitos para uma função pura é "impura".
Avaliação lenta x avaliação rápida
Avaliação lenta
A avaliação lenta não avalia os argumentos da função a menos que seus valores sejam necessários para avaliar a própria chamada da função.
Em outras palavras, as expressões são avaliadas apenas quando ocorre a avaliação de outra expressão que depende da expressão atual.
A versão lenta das avaliações permite que os programas calculem estruturas de dados que são potencialmente infinitas sem travar.
Avaliação rápida
A avaliação ávida — também conhecida como avaliação estrita — sempre avalia completamente os argumentos da função antes de invocar a função. Em outras palavras, uma expressão é avaliada assim que é vinculada a uma variável.
Declarativo x imperativo
Programação declarativa
Programas declarativos expressam um conjunto de operações sem revelar como são implementadas ou como os dados fluem através delas. Eles se concentram em "o que" o programa deve realizar (usando expressões para descrever a lógica) em vez de "como" o programa deve alcançar o resultado.
Um exemplo de programação declarativa é o SQL. As consultas SQL são compostas por instruções que descrevem como deve ser o resultado de uma consulta, enquanto abstraem o processo interno de como os dados são recuperados:
SELECT EMP_ID, FIRST_NAME, LAST_NAMEFROM EMPLOYEES WHERE CITY = ‘SAN FRANCISCO’ ORDER BY EMP_ID;
Aqui está um exemplo de código declarativo:
Programação imperativa
A programação imperativa concentra-se em descrever como um programa deve alcançar um resultado usando instruções que especificam o fluxo de controle ou alterações de estado. Ela usa uma sequência de instruções para calcular um resultado.
Aqui está um exemplo de código imperativo:
Com estado x sem estado
Um estado (em inglês, state) é uma sequência de valores calculados progressivamente, que contém os resultados intermediários de um cálculo.
Com estado
Programas com estado (do inglês., stateful) têm algum mecanismo para acompanhar e atualizar o estado. Eles têm alguma memória do passado e lembram transações anteriores que podem afetar a transação atual.
Sem estado
Programas sem estado (do inglês, stateless), por outro lado, não acompanham o estado. Não há memória do passado. Cada transação é realizada como se estivesse sendo feita pela primeira vez. Programas stateless darão a mesma resposta para o mesmo pedido, função ou chamada de método — todas as vezes.
Funcional x orientada a objetos
Funcional
A programação funcional é um paradigma que enfatiza o uso de funções. O objetivo da programação funcional é usar funções para abstrair fluxos de controle e operações em dados, evitando efeitos colaterais.
Então, a programação funcional usa funções puras e evita dados mutáveis, o que, por sua vez, fornece transparência referencial.
Uma função tem transparência referencial quando você pode substituir livremente uma expressão por seu valor e não mudar o comportamento do programa. Dito de um modo um pouco diferente: para uma determinada entrada, ele sempre retorna os mesmos resultados.
Alguns exemplos de linguagens que enfatizam a programação funcional incluem Haskell, Lisp, Clojure e Elm. Você pode, contudo, usar conceitos de programação funcional na maioria das linguagens, incluindo em JavaScript.
Orientada a objetos
O paradigma de programação orientada a objetos enfatiza o uso de objetos. Isso resulta em programas que são feitos de objetos que interagem entre si. Esses objetos podem conter dados (na forma de campos ou atributos) e comportamento (na forma de métodos).
É um estilo de particionamento (ou encapsulamento) do estado de um programa através de objetos para tornar a análise do efeito das mudanças tratável [1].
Além disso, programas orientados a objetos usam herança e/ou composição como seus principais mecanismos para reutilização de código. Herança significa que uma nova classe pode ser definida em termos de classes existentes especificando apenas como a nova classe é diferente. Representa um relacionamento "é um" (por exemplo, uma classe Pássaro
que estende uma classe Animal
). A composição, por outro lado, é quando as classes contêm instâncias de outras classes que implementam a funcionalidade desejada. Representa um relacionamento "tem um" (por exemplo, uma classe Pássaro
tem uma instância de uma classe Asa
como membro).
O polimorfismo também é um mecanismo importante para a reutilização de código na programação orientada a objetos. É quando uma linguagem pode processar objetos de maneira diferente dependendo de seu tipo de dados ou classe.
Alguns exemplos de linguagens que enfatizam a programação orientada a objetos incluem Java, C++, Ruby. Novamente, você pode aplicar esses conceitos na maioria das linguagens, incluindo em JavaScript.
Determinístico x não determinístico
Determinístico
Programas determinísticos sempre retornam o mesmo resultado sempre que são chamados com um conjunto específico de valores de entrada e o mesmo estado fornecido.
Não determinístico
Programas não determinísticos podem retornar resultados diferentes cada vez que são chamados, mesmo com o mesmo conjunto específico de valores de entrada e estado inicial.
O não determinismo é uma propriedade de qualquer sistema concorrente — ou seja, qualquer sistema em que várias tarefas podem acontecer ao mesmo tempo, executando em diferentes threads. Um algoritmo concorrente que está alterando o estado pode se comportar de maneira diferente a cada vez, dependendo de qual thread o agendador decidir executar.
Por exemplo:
declare Xthread X=1 endthread X=2 end
A ordem de execução das duas threads não é fixa. Não sabemos se X será vinculado a 1 ou 2. O sistema escolherá durante a execução do programa, sendo livre para escolher qual thread executar primeiro.
Outro exemplo de não determinismo:
Conclusão
Como sempre, seu feedback e sua contribuição são realmente importantes para mim. Você também pode conferir a apresentação do Prezi que criei para este artigo.
[1] Um agradecimento especial a Kent Beck por sua contribuição para este artigo.