Pensando funcional: Imutabilidade

--

Olá devs,

Aqui no Pravaler, estamos mudando parte da nossa stack para Elixir. Por este motivo, nos últimos tempos venho estudando os conceitos e paradigmas funcionais. Meu objetivo é compartilhar um pouco destes conhecimentos adquiridos, por isso resolvi criar uma série de artigos referente a estes conceitos.

O primeiro conceito que trago para vocês se trata da imutabilidade. Ao realizar uma busca no dicionário o significado da palavra imutabilidade teremos a seguinte descrição:

“Característica daquilo que é imutável; que não muda ou não pode ser modificado; designação de inalterabilidade ou invariabilidade. “

Se recorremos ao conceito de imutabilidade na teologia, Wayne Grudem em seu livro Teologia sistemática descreve o seguinte:

Imutabilidade é o atributo divino segundo o qual Deus não pode mudar.”

Voltando para a imutabilidade dentro do contexto da engenharia de software, o conceito surgiu antes mesmo até das primeiras linguagens de programação e foi derivado do estudo sobre fundamentos da matemática feito pelo Alonzo Church.

Em 1936, Church publicou apenas as partes deste estudo voltado para a computação, que tempos depois ficou conhecido como cálculo lambda.

Agora você deve estar se perguntando.

“Ok, mas como funciona essa tal de imutabilidade na programação ?”

Assim como os conceitos de imutabilidade descritos acima, o grande ponto é a manutenção do estado original das coisas, ou seja, uma vez definido um determinado estado ele não poderá ser mais modificado.

Observe o código:

No código acima está definido o atributo pessoa, que é um array com os elementos idade e nome. Contém também a implementação de dois métodos, o primeiro modifica e retorna o elemento idade e o segundo apenas retorna o elemento idade.

Nas linhas 17 e 18, podemos observar as saídas dos dois métodos e os retornos deles são exatamente os mesmos, isso ocorre por que o método modifica() ao transformar o elemento idade altera o estado do atributo, que passar a ter o elemento idade alterado, já se analisamos o trecho de código que contém as mesmas características, porém escrito em uma linguagem funcional, teremos o seguintes comportamentos:

Observando as saídas dos dois métodos neste caso, podemos perceber que são diferentes, isso acontece por que o método modifica() realiza a transformação do elemento idade, porém não altera o estado do atributo pessoa, por este motivo a saída do getIdade() é exatamente o mesmo valor definido no início do módulo, caracterizando assim a imutabilidade do atributo pessoa.

Por que Imutabilidade?

Do ponto de vista arquitetural, existe uma preocupação com a mutabilidade das variáveis em aplicações com linguagens imperativas e orientadas a objetos, esta preocupação é principalmente relacionada com os problemas de race conditions e deadlock.

Já em uma aplicação escrita em linguagem puramente funcional, ocasiões onde as variáveis são imutáveis, este tipo de problema não ocorre já que o estado interno não é alterado.

Portanto, se queremos escrever sistemas robustos sem nos preocuparmos com estes tipos de problemas de concorrência, vale considerar a utilização de linguagens funcionais (Elixir, Clojure, etc) e usufruir de algumas vantagens como maior tolerância a falhas e sistemas com manutenção mais fácil.

Então uma variável não deve ter o seu valor alterado?

Pode sim, mas desde que seja no contexto correto, Robert C. Martin em seu livro clássico de arquitetura o Clean Architecture, propõe uma abordagem interessante que ele chamou de “Segregação de Mutabilidade”. Observe o desenho abaixo:

A ideia é realizar uma segregação entre os componentes ou serviços imutáveis dos componentes mutáveis.

Os componentes imutáveis realizam as operações de forma funcional, sem usar variáveis que alteram o seu estado e quando precisam realizar alguma operação em que é necessário alterar o estado de uma variável, eles realizam a comunicação com um ou mais componentes, que permitem a utilização de variáveis mutáveis. Para que estes componentes mutáveis evitem os problemas de concorrência que citei anteriormente, é usado um tipo de memória transacional para tratar essas variáveis mutáveis, funcionando muito parecido como um banco de dados ao tratar os seus registros em disco. Este conceito de arquitetura que Martin propôs, também vem de encontro com o conceito de funções puras e funções impuras, iremos aprofundar mais sobre este conceito nos próximos artigos.

No código abaixo eu criei dois módulos, um que armazena o estado e altera este estado quando solicitado e outro que solicita as alterações.

O ModuloX através do método iniciarContador() seta o estado inicial do módulo Counter atribuindo o valor 0 e quando o método alterar() é executado o estado do módulo Counter é incrementado com +1, ou seja alterando o seu estado inicial, portanto podemos dizer que Counter é mutável.

Considerações

Estudar uma nova linguagem e ainda sendo do paradigma funcional é muito enriquecedor para o meu aprendizado contínuo em engenharia de software e provavelmente para você também vai ser.

Para os próximos artigos abordarei mais conceitos referente ao paradigma funcional.

Até a próxima….

--

--