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.

GD1KhgaCFzdaiATtb-MnKj3L-ZXUiIoydrc8
À direita: os tipos de x e y são verificados no momento da compilação para garantir que as duas variáveis são do tipo int/Abaixo: quando você instancia a classe Point, os tipos de x e de y são verificados no momento da compilação para garantir que são do tipo int

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.

r0NYTBqJGSc8XLHqK8Et-rMW0tkOd3VI7I5u
À direita: os tipos de x e y são determinados no momento da execução/Abaixo: quando você instancia a classe Point, os tipos de x e de y não são verificados. Se você passar um valor de string para y, o programa o aceitará (e pode lançar um erro de tempo de execução se y for usado indevidamente)

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.

qQuFtQHI0nVeCYYPFiwmtPXD0I0VtUfQS3VH

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.

iKBgNAycyEVpLytn2tivoY3U0wN-osYulEJr
Adicionar a string "4" à soma de 4 + 4 = 8 resulta na string "8" + "4" = "84". Assim, adicionar a string a um número causou o casting (a conversão) do número 8 para uma string.

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.

D3tF-qLMqDUXzFdHRRXy9jZm5ZrZ-DJVxi-p

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.

lKF181TAlPPvUz0ln8VLksubyCYCbs8ocB73
A variável person é mutável. Assim, podemos atualizar a propriedade age (idade)

Funções puras x impuras

Funções puras

Uma função pura tem duas qualidades:

  1. Ela depende apenas da entrada fornecida — e não de nenhum estado externo que possa mudar durante sua avaliação ou entre chamadas.
  2. Ela não causa nenhum efeito colateral semanticamente observável, como modificar um objeto global ou um parâmetro passado por referência.
ir5sDGMoxckbC-wpR8JikApucz6DLkkPzGZx
A função depende somente das variáveis passadas explicitamente a ela, não alterando estados externos nem os argumentos passados a ela

Funções impuras

Qualquer função que não atenda a esses dois requisitos para uma função pura é "impura".

J02vTF9q3BE9lIYoi2jV1QbJAqjRcMZJBw8L-1
A função depende da variável global PI e altera o argumento de entrada radii

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.

uxczq1DSf-SYP34fDk02eeBqbbEFaqwsUwP0
Acima: vamos supor que temos uma função que filtra todos os números ímpares de 1 até o infinito e recebe os dez primeiros elementos/Abaixo: com a avaliação lenta, isso não é executado até que a operação que depende disso, take, é chamada.

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.

SHUGZI2HS4n8mSPAWoUfWySxt8D4X0N5CXu-
Com a avaliação rápida, a função range tentará executar por inteiro antes de enviar seu resultado a take, o que leva a um loop infinito.

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:

KJkzhjbfo5EYE2flsvDS9gT0JqAIcKcfklcA
Usa funções de ordem superior para expressar o que o programa faz, não como o faz.

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:

nWC5LzuByaWllxXsVXJS1pcYokOWlKxkZor2
Informa ao programa como executar a tareda usando loops e instruções if-else

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.

2MCDlKSDdS9imKRxHECiksWO2jjxjeV1ndVB
Essa função tem conhecimento do passado. Ela sabe o valor da variável count.

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.

O4aqQxCiZF-tsYmo4yl34wrLs8wGOsWODvy3
Essa função não tem conhecimento do passado. A variável count é passada como um parâmetro e a função simplesmente retorna um resultado a partir daquilo que é passado para ela.

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.

N8MHF9tH7WY137DollJShA9icCiaduLFJS9O
Comentários em ordem (de cima para baixo): Métodos de instâncias usam "this" para acessar os dados do objeto/Herança/Dados mutáveis

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.

QZcUv8Ergv0DU4N3SSrR6SFbajhNRXzagcTr
Não importa o número de vezes que invocarmos a função add, os resultados sempre serão os mesmos.

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:

V0yGj2uPPh5HUKf25kTgQiGZwLG8eAsRjkXM
Temos dois resultados possíveis para os valores resultantes de a e de b, dependendo de qual chamada de API é retornada primeiro./Se a primeira chamada de API retorna antes da segunda, foo será invocada antes de bar, a será 15 e b será 3. Caso contrário, a será 13 e b será 2.

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.