Na programação, quais são os benefícios da transparência referencial ?
A TR faz uma das principais diferenças entre paradigmas funcionais e imperativos e é frequentemente usada pelos defensores do paradigma funcional como uma clara vantagem sobre o imperativo; mas em todos os seus esforços, esses advogados nunca explicam por que é um benefício para mim como programador .
Claro, eles terão suas explicações acadêmicas sobre o quão "puro" e "elegante" é, mas como isso o torna melhor do que um código menos "puro"? Como isso me beneficia na minha programação do dia-a-dia?
Nota: Esta não é uma duplicata de O que é transparência referencial? O último aborda o tópico do que é RT, enquanto essa pergunta aborda seus benefícios (que podem não ser tão intuitivos).
fonte
Respostas:
O benefício é que funções puras facilitam o raciocínio do seu código. Ou, em outras palavras, os efeitos colaterais aumentam a complexidade do seu código.
Veja um exemplo de
computeProductPrice
método.Um método puro solicitaria uma quantidade de produto, uma moeda etc. Você sabe que sempre que o método é chamado com os mesmos argumentos, sempre produz o mesmo resultado.
Um método não puro será mais complexo para usar e depurar. Uma vez que depende do estado das variáveis que não sejam os argumentos e possivelmente alterá-las, significa que poderia produzir resultados diferentes quando chamados várias vezes, ou não ter o mesmo comportamento quando não chamado ou chamado muito cedo ou muito tarde.
Exemplo
Imagine que existe um método na estrutura que analisa um número:
Não possui transparência referencial, porque depende de:
A variável de ambiente que especifica o sistema de numeração, que é a Base 10 ou outra coisa.
A variável na
math
biblioteca que especifica a precisão dos números a serem analisados. Portanto, com o valor de1
, a análise da string"12.3456"
dará12.3
.A cultura, que define a formatação esperada. Por exemplo, com
fr-FR
, a análise"12.345"
dará12345
, porque o caractere de separação deve ser,
, não.
Imagine como seria fácil ou difícil trabalhar com esse método. Com a mesma entrada, é possível obter resultados radicalmente diferentes, dependendo do momento em que você chama o método, porque algo em algum lugar mudou a variável de ambiente ou mudou a cultura ou definiu uma precisão diferente. O caráter não determinístico do método levaria a mais erros e a mais pesadelos de depuração. Ligar
math.parse("12345")
e obter5349
como resposta, já que algum código paralelo estava analisando números octais não é bom.Como consertar esse método obviamente quebrado? Introduzindo transparência referencial. Em outras palavras, se livrando do estado global e movendo tudo para os parâmetros do método:
Agora que o método é puro, você sabe que não importa quando você chama o método, ele sempre produzirá o mesmo resultado para os mesmos argumentos.
fonte
packet = socket.recv()
referencialmente transparente derrota o ponto da função.invariant
? Ou são os mesmos deen_us
, nesse caso, por que se preocupar, ou correspondem a algum outro país; nesse caso, qual e por que esse em vez deen_us
, ou eles têm regras específicas que não correspondem a nenhum país de qualquer maneira , o que seria inútil. Não existe realmente uma "resposta verdadeira" entre12,345.67
e12 345,67
: quaisquer "regras padrão" funcionarão para alguns países e não funcionarão para outros.12345
analisa como 1234512 345
ou12,345
ou12.345
é um erro.12.345
analisado como um número de ponto flutuante invariável sempre gera 12.345, de acordo com a convenção da linguagem de programação de usar. como o separador decimal. As strings são classificadas por seus pontos de código Unicode e diferenciam maiúsculas de minúsculas. E assim por diante.Você costuma adicionar um ponto de interrupção a um ponto do seu código e executar o aplicativo no depurador para descobrir o que está acontecendo? Se o fizer, é porque você não está usando transparência referencial (RT) em seus projetos. E, portanto, é necessário executar o código para descobrir o que ele faz.
O ponto principal para a RT é que o código é altamente determinístico, ou seja, você pode ler o código e descobrir o que ele faz, sempre, para o mesmo conjunto de entradas. Depois de começar a adicionar variáveis mutantes, algumas das quais têm escopo além de uma única função, você não pode simplesmente ler o código. Esse código deve ser executado, na sua cabeça ou no depurador, para descobrir como ele realmente funciona.
Quanto mais simples o código é a leitura e o raciocínio, mais simples é a manutenção e a identificação de bugs, economizando tempo e dinheiro para você e seu empregador.
fonte
As pessoas usam o termo "mais fácil de raciocinar", mas nunca explicam o que isso significa. Considere o seguinte exemplo:
São
result1
eresult2
iguais ou diferentes? Sem transparência referencial, você não tem ideia. Você precisa realmente ler o corpo defoo
para ter certeza, e possivelmente o corpo de qualquer funçãofoo
, e assim por diante.As pessoas não percebem esse fardo porque estão acostumados a ele, mas se você trabalhar em um ambiente puramente funcional por um mês ou dois, depois voltar, sentirá isso, e é um grande negócio .
Existem muitos mecanismos de defesa que as pessoas fazem para solucionar a falta de transparência referencial. Para o meu pequeno exemplo, talvez eu queira ficar
result1
na memória, porque não saberia se isso mudaria. Então eu tenho código com dois estados: antesresult1
foi armazenado e depois. Com transparência referencial, posso apenas recalcular com facilidade, desde que o recálculo não consuma tempo.fonte
result1
eresult2
são os mesmos. Outro aspecto importante é que, sefoo("bar", 12)
for referencialmente transparente, você não precisa se perguntar se essa chamada produziu efeitos em outro lugar (defina algumas variáveis? Excluiu um arquivo? Qualquer que seja).Eu diria: a transparência referencial não é boa apenas para programação funcional, mas para todos que trabalham com funções porque seguem o princípio de menor espanto.
Você tem uma função e pode raciocinar melhor sobre o que faz, porque não há fatores externos que precisam ser levados em consideração; para uma determinada entrada, a saída será sempre a mesma. Mesmo na minha linguagem imperativa, tento seguir esse paradigma o máximo possível, a próxima coisa que se segue automaticamente é: funções pequenas e fáceis de entender, em vez das funções terríveis de mais de 1000 linhas nas quais eu às vezes corro.
Essas grandes funções fazem mágica e eu tenho medo de tocá-las porque podem quebrar de maneiras espetaculares.
Portanto, funções puras não são apenas para programação funcional, mas para todos os programas.
fonte