O que é uma Função Pura? Código em Javascript.
Esse artigo é uma tradução livre de What Is a Pure Function in JavaScript?, por Yazeed Bzadough.
Funções puras são blocos atômicos na programação funcional. São adoradas por sua simplicidade e testabilidade.
Esse post abordará um rápido checklist para te mostrar se uma função é pura ou não.
O Checklist
Uma função precisa ter 2 (duas) coisas para ser considerada “pura”:
- A mesma entrada (input) sempre retorna o mesmo saída (output);
- Não possui efeitos colaterais (side-effects).
Vamos entender cada um deles.
1. Mesma entrada => Mesma saída
Compare isso:
const add = (x, y) => x + y;add(2, 4); // 6
Com isso:
let x = 2;const add = (y) => {
x += y;
};add(4); // x === 6 (resultado ao executar na primeira vez)
add(4); // x === 10(resultado ao executar na segunda vez)
Funções Puras = Resultados consistentes
O primeiro exemplo retornará um valor baseado nos parâmetros enviados, independentemente de onde/quando for chamado.
Se você passar 2
e 4
, sempre receberá 6
.
Nada afeta o resultado.
Funções Impuras = Resultados inconsistentes
O segundo exemplo não retorna nada. Ele depende do estado compartilhado para fazer seu trabalho incrementando uma variável fora de seu próprio escopo.
Esse padrão é pode ser um baita pesadelo de um desenvolvedor.
O estado compartilhado introduz uma dependência temporal. Você obtém diferentes resultados dependendo de quando invocar a função. Na primeira vez o resultado será 6
, na segunda será 10
, e assim por diante.
Qual delas possui perguntas mais simples de responder?
Qual delas é a menos provável de produzir bugs que acontecem apenas sob certas condições?
Qual delas é a mais provável de ter sucesso em um ambiente multi-threaded onde as dependências de tempo podem quebrar o sistema?
Definitivamente a primeira.
2. Sem Efeitos Colaterais (Side-Effects)
Esse teste é um checklist. Alguns exemplos de efeitos colaterais (side-effects) são:
- Mutação da entrada;
console.log
;- Chamadas HTTP (AJAX/fetch);
- Modificar o filesystem (fs);
- Consultar o DOM.
Basicamente, qualquer trabalho executado por uma função que não esteja relacionado ao cálculo de uma saída (output).
Eu recomendo você assistir esse trecho do Uncle Bob Martin sobre o problema do estado. Começa lá pelos 15 minutos.
Aqui abaixo está uma função impura com um efeito colateral.
Não é tão ruim
const dobroImpuro = (x) => {
console.log('dobrando', x); return x * 2;
};const resultado = dobroImpuro(4);console.log({ resultado });
O console.log
é o efeito colateral aqui, mas com toda a praticidade, não vai nos prejudicar. Ainda vamos obter as mesmas saídas, dadas as mesmas entradas.
Isso, entretanto, poderá causar um problema.
“Impuramente” modificando um objeto
const assocImpuro = (key, value, object) => {
object[key] = value;
};
const pessoa = {
nome: 'Bob'
};
const resultado = assocImpuro('tamanhoDoTenis', 40, pessoa);console.log({
pessoa,
resultado
});
A variável pessoa
foi alterada para sempre pois nossa função introduziu uma instrução de atribuição.
Estado compartilhado significa que o impacto do assocImpuro
não é mais totalmente óbvio. Entender seu efeito em um sistema agora envolve rastrear todas as variáveis que já foram tocadas e conhecer suas histórias.
Estado compartilhado = dependências temporais
Nós podemos purificar a função assocImpuro
simplesmente retornando um novo objeto com as propriedades desejadas.
Purificando
const assocPuro = (key, value, object) => ({
...object,
[key]: value
});const pessoa = {
name: 'Bob'
};const resultado = assocPuro('tamanhoDoTenis', 40, pessoa);console.log({
pessoa,
resultado
});
Agora a função assocPuro
retorna um resultado testável e nós nunca nos preocuparemos se isso mudou algo em outro lugar.
Você pode inclusive fazer o código abaixo e permanecer com a função pura:
Outro modo puro
const assocPuro = (key, value, object) => {
const novoObjeto = { ...object }; novoObjeto[key] = value; return novoObjeto;
};const pessoa = {
name: 'Bob'
};const resultado = assocPuro('tamanhoDoTenis', 40, pessoa);console.log({
pessoa,
resultado
});
Mutar seu objeto pode ser perigoso, mas mutar uma cópia não tem problema.
Nosso resultado final ainda é uma função testável e previsível que funciona independentemente de onde/quando você a chama.
A mutação está limitada a esse pequeno escopo e você ainda está retornando um valor.
Resumo
- Uma função pura é livre de efeitos colaterais (side-effects) e retorna sempre a mesma saída, dada a mesma entrada;
- Efeitos colaterais (side-effects) incluem: mutação da entrada (input), chamadas HTTP, escrita no disco, escrever algo na tela;
- Você pode tranquilamente clonar, então mutar sua entrada. Apenas deixe o original intocável;
- A syntax de Spread (
...
) é o modo mais fácil de clonar objetos e arrays.
Meu curso gratuito
Esse tutorial foi retirado do meu curso gratuito do site Educative.io, Functional Programming Patterns With RamdaJS!
Por favor, considere compartilhar ou mostrar isso a alguém se gostou do conteúdo.
É repleto de lições, gráficos, exercícios e amostras de código executáveis para ensinar um estilo básico de programação funcional usando o RamdaJS.
Eu também estou no Twitter se desejar conversar. Até a próxima!
Se cuida,
Yazeed Bzadough
http://yazeedb.com/