Transparência Referencial, Pureza e Imutabilidade
A Programação Funcional é um paradigma da computação em que as instruções são escritas como se fossem expressões matemáticas e, portanto, herdam características tais como dados e estados imutáveis e usam uma abordagem mais declarativa que imperativa em suas estruturas.
Uma expressão, tal como uma função na Programação Funcional, deve ter apenas um propósito e seu resultado depende exclusivamente dos argumentos recebidos. Sendo assim, sempre que um mesmo conjunto de argumentos é passado a uma função o resultado desta será sempre o mesmo.
const soma = (a, b) => a + b
A Programação Funcional não é algo novo, ela tem suas origens no Cálculo Lambda que teve um papel fundamental na concepção e estruturação de várias linguagens de programação. Suas vantagens são, entre outras, a maior previsibilidade e testabilidade do código, facilitando a manutenção de projetos de qualquer tamanho e nível de complexidade.
Transparência Referencial
Para que uma função não sofra nem exerça influências implícitas do escopo exterior a ela, nenhum dado pode ser captado por ela a não ser através de seus próprios argumentos. Desta forma, coisas como variáveis globais não devem ser usadas e dados devem ser coletados por outras estruturas e então passados para a função. Isso garante que a função terá sempre o mesmo resultado sempre que forem passados os mesmos argumentos. A este princípio da Programação Funcional damos o nome de Transparência Referencial.
Não transparente:
let x = 1 const addOne = () => { x++ } addOne() // x == 2
Transparente:
let x = 1 const addOne = x => x + 1 addOne(x) // x == 1
Já pensou na possibilidade de se fazer cache de resultado de funções? Isso é uma técnica chamada “memoização” (ou memoization), e é uma das vantagens da transparência referencial. Como todos os dados consumidos pela função veem de seus argumentos é possível usar estes argumentos (ou uma chave gerada a partir deles) como identificador de cache e o resultado da função como valor. No exemplo acima esse cache não faria muito sentido, mas em uma função que executa um processo mais custoso a memoização pode ser usada como estratégia para melhorar a performance.
Pureza
Além de não consumir nenhum dado exterior, sempre que possível, uma função na Programação Funcional não pode produzir nenhum “efeito colateral”. Ou seja, persistência de dados, escrita no sistema de arquivos ou renderizações de interface devem ser evitados dentro de expressões funcionais, bem como alterações em referências a variáveis usadas como argumentos da função. Isso é o que chamamos de Pureza ou Funções Puras.
Não puro:
let name = 'World' const sayHello = () => { // Lê dados de uma variável externa e a modifica. name = 'Hello ' + name + '!' // Efeito colateral. console.log(name) } sayHello()
Puro:
let name = 'World' // Sem efeito colateral. const hello = name => 'Hello ' + name + '!' // Transparência referencial: 'name' é um argumento. console.log(hello(name))
Imutabilidade
Para escrever peças de software como funções matemáticas, imutabilidade é um conceito chave a se considerar: precisamos trabalhar com estruturas mais sofisticadas que apenas números, mas, ao mesmo tempo, manter as mesmas características de imutabilidade dadas a estas estruturas primitivas.
let x = 1 let y = x y = 2 // x == 1 (imutável) let a = {} let b = a b.foo = true // a == { foo: true } (mutável) let c = {bar: 'bar'} // Torna o objeto imutável. Object.freeze(c) // A próxima linha não tem efeito. c.baz = 'baz' // c = {bar: 'bar'} (imutável) // Cria um novo objeto. let d = Object.assign({}, c, { baz: 'baz' }) // Torna o objeto imutável. Object.freeze(d) // c == {bar: 'bar'} (imutável) // d == {bar: 'bar', baz: 'baz'}
Este foi o primeiro da série de artigos sobre Programação Funcional. No próximo desvendaremos mais conceitos deste paradigma que está revolucionando a forma de escrever aplicações. Nesse meio tempo, fique à vontade para dar seu feedback nos comentários.
Obrigado pela leitura e até logo!