Gotas de Elixir – Imutabilidade

Programação Funcional não é nada novo e na verdade surgiu bem antes da Orientação a Objetos[1][2]. Mas hoje em dia elas vem se tornando bem famosas, especialmente pelo fato de que quase todo sistema utiliza-se de processamento paralelo.

Quando tínhamos uma capacidade computacional bem limitada, a programação funcional não fazia muito sentido, já que para evitar alterações no estado interno mais recurso é necessário. Mas esse tempo já passou e agora as linguagens funcionais estão voltando ao mundo comercial.

A principal vantagem de um programa desenvolvido em uma linguagem funcional é que ele não altera o seu estado interno. Assim, podemos escalar um programa funcional em vários cores paralelos, sem se preocupar com condição de corrida, deadlocks etc.

O que é o estado interno?

Veja as seguintes linhas de código em ruby:

total = 1900
operacao_maluca(total)
#podemos afirmar que o valor de total ainda é 1900?

Como não temos nenhuma garantia de o método operacao_maluca não altera seu argumento, não podemos ter certeza que o valor de total ainda é 1900. Por isso dizemos que o estado interno pode ser alterado. Se dois processos executarem a operaca_maluca ao mesmo tempo, fica impossível descobrir o que acontece com total.

Já esse mesmo pedaço de código escrito em Elixir, podemos ter certeza que o valor de total continuará sendo 1900, pois variáveis em elixir são imutáveis. Mas isso não quer dizer que total nunca poderá ser atualizado!

total = 1900
total = 1700
IO.puts total # 1900 ou 1700?

Explorando a Imutabilidade

Opa! Se variáveis são imutáveis, então total vai continuar sendo 1900 certo? Não! Como total é uma variável a referência para que ela aponta pode mudar! Desde que seja no contexto correto.

Observe o código a seguir e tente adivinhar o que é exibido na tela a cada execução de IO.puts:

total = 1900
defmodule Mutante do
  def mutar(valor) do
    valor = 1
    IO.puts valor # Aqui será exibido 1 ou 1900?
    valor
  end
end
Mutante.mutar(total)
IO.puts total # E aqui? 1 ou 1900?
total = Mutante.mutar(total)
IO.puts total # E agora, 1 ou 1900?

No primeiro IO.puts, dentro da função Mutante.mutar, será exibido 1! Temos uma variável definida como argumento e estamos alterando a sua referência. Antes ela apontava para uma parte da memória que continha 1900, agora aponta pra outra que contém 1.

Já no segundo IO.puts, depois da execução da função, deveria ser exibido 1, já que mudamos a referência dentro da função, certo? Não! Na verdade, fora da função, total continua apontado para 1900, a referência não pode ser alterada em outro lugar.

Para mudar o valor de total o que fazemos é mudar sua referência, como mostrado no último IO.puts, que agora sim mostrará total como 1. Então ser imutável não quer dizer que o valor nunca mudará, mas sim que eles está protegido de mudanças externas.

É possível ter Imutabilidade em linguagens não funcionais?

Claro que sim! Imutabilidade é uma decisão de design da aplicação, o que Elixir faz é forçar que isso nunca aconteça. Então mesmo que você use uma biblioteca desconhecida, terá certeza que valores não serão mudados inconscientemente.

A Imutabilidade é na verdade uma boa prática mesmo em linguagens Orientada a Objetos. Se o seu método altera um argumento, existem altas chances de surgirem bugs devido a esse efeito colateral. Sério, se você faz isso, pare agora. O seu eu do futuro vai agradecer.

O problema é que, por mais que você tome a decisão sensata de nunca mudar o estado de um argumento, não dá pra garantir que aquela gem que você usa em todos os projetos também segue essa ideia.

A maneira de garantir a imutabilidade é criando novas referências, o que leva ao ponto que comentei no começo do post sobre utilização de recursos. Numa época em que todo o processamento do mundo era menor que o do seu celular, isso fazia uma grande diferença. Mas agora isso não é mais um problema tão grande.

Gostou do post? Compartilhe com suas amigas e amigos! Se quiser saber mais sobre Elixir, veja os outros posts da série Gostas de Elixir!

Referências

[1] https://en.wikipedia.org/wiki/Functional_programming

[2] https://en.wikipedia.org/wiki/Object-oriented_programming

Deixe um comentário