Paradigmas de Programação II — Programação Imperativa

Um olhar sobre o paradigma imperativo sob a ótica do javascript

Vinícius Andrade
4 min readDec 11, 2015

O primeiro dos paradigmas que vamos analisar em nossa série é o paradigma imperativo.

A disciplina e as ideias que sustentam o paradigma imperativo estão intimamente ligados à tecnologia de hardwares e às ideias de Von Neumann.

“Primeiro faça isso, depois faça aquilo.”

Essa frase curta consegue descrever bem a essência do imperativo. Aqui é importante notar como os comandos são de semântica simples e objetiva. Note-se também a importância da variável tempo no resultado da execução de comandos: primeiro faça isso e depois faça aquilo é diferente de primeiro faça aquilo e depois faça isso.

Assim, podemos dizer que o paradigma imperativo vai se preocupar em descrever os acontecimentos de seu programa como uma sequência de operações. Nessa descrição, os conceitos de loop e bloco são muito importantes.

Ele vai usar variáveis nomeadas, que detêm valores que são alterados pelas expressões descritas na sequência de operações. A ordem de execução aqui é essencial.

Como os valores são alterados ao longo do programa por uma série de operações, a ordem em que cada uma dessas operações é executada muda completamente o resultado do programa. Isso implica na responsabilização do programador pelo controle do seu programa.

Temos então uma definição conceitual do que é programar de forma imperativa.

Um exemplo de código imperativo em javascript:

const numbers = [1,2,3]
let doubled = []

for(let i = 0; i < numbers.length; i++) {
let newNumber = numbers[i] * 2
doubled.push(newNumber)
}
console.log(doubled) //=> [2,4,6]

Se você já procurou algo relacionado ao assunto, já deve ter visto definições muito parecidas. Olhando somente para o que foi descrito acima, não ficam claros os aspectos conceituais que de fato nos servem para separar, em essência, o paradigma imperativo dos demais.

Cabe lembrar que historicamente, os demais paradigmas derivam de alguma forma deste, e dessa forma sempre haverá um ou vários pontos em comum a dois paradigmas quaisquer, o que acaba homogeneizando a análise e tornando difícil discernir um do outro.

Como nosso objetivo concreto é ter uma base teórica sólida para comparar de forma clara os paradigmas entre si, vamos abordar a questão por um aspecto que nos dê um bom ângulo para essa análise.

Aprofundando o olhar

Em uma tonelada de artigos por aí você já deve ter lido a expressão “estado do programa” e outras derivadas. Mas, o que é o estado de um programa?

Uma pesquisa rápida na wikipedia vai te dizer algo do tipo:

Um programa armazena dados em variáveis, que representam espaços lógicos dentro da memória do computador. O conteúdo dos dados armazenados nesses espaços em qualquer instante da execução do programa define o seu estado.

A compreensão do estado do seu programa e o seu controle sobre ele é um ponto importante na hora de decidir de que forma você vai escrever o seu código. E é dentro dessa ótica que podemos observar o paradigma imperativo de forma mais construtiva.

Em Ciências da Computação, diz-se que, se a substituição de uma determinada expressão pelo valor assinado à ela somente é valido em determinado ponto do programa, então essa expressão não é “referencialmente transparente”. Uma expressão é dita transparente referencial quando a substituição dela pelo seu valor em qualquer ponto do programa não altera o comportamento deste.

Transparência referencial implica numa série de atributos que você já deve ter lido em artigos que tratam de programação funcional: imutabilidade, efeitos colaterais, funções puras, etc. Isso será tratado mais à frente, quando estivermos tratando de Programação Funcional em si.

Mas fato é que o paradigma imperativo não oferece transparência referencial. Expressões podem ter associadas à elas diferentes valores ao longo da execução do programa. Esse aspecto é determinante na hora de compararmos esse paradigma com os demais.

Observe novamente o exemplo acima. Se no momento de realizarmos o log, substituíssemos a variável doubled pelo valor que foi passado para ela, teríamos como resultado um array vazio.

Ter um controle firme sobre os estados do seu programa pode de fato ser uma vantagem. Análise estática de programas e otimização de código se torna uma tarefa factível nesse contexto, e de uma forma geral você pode construir um código de mais qualidade, mas isso vai depender MUITO do problema que você têm à mão. Por exemplo:

A descrição de operações que são de fato melhor descritas como uma sequência de passos pode ficar estranha e pouco expressiva.

Além disso, quando há transparência referencial fica difícil determinar se determinada expressão é uma coisa ou a referência a uma coisa, algo que o paradigma imperativo faz muito bem.

No Javascript

Quando nos voltamos para o javascript, onde vários idiomas são possíveis, verificamos um importante aspecto quando pensamos em abordagem: performance.

A aproximação que o paradigma imperativo tem da linguagem de hardware acaba facilitando a sua implementação, o que sempre resulta em uma melhor performance. Ex.: https://jsperf.com/map-vs-native-for-loop/34

É justamente da performance dessa aproximação (linguagem — hardware) que o projeto do WebAssembly se vale.

Mas fato é que no cotidiano do desenvolvedor javascript, quase sempre estaremos abordando programas sob uma ótica multi-paradigma. Isso é fruto da natureza flexível do js.

De uma forma geral, podemos dizer que todos os paradigmas proporcionam mecanismos para mitigar seus próprios efeitos negativos. Mas sempre será papel de quem escreve o código pesar o que é mais importante para o seu produto: performance, legibilidade de código, manutenibilidade, testabilidade e outros.

--

--