O que significa o termo transparência referencial ? Ouvi isso descrito como "significa que você pode substituir iguais por iguais", mas isso parece ser uma explicação inadequada.
O que significa o termo transparência referencial ? Ouvi isso descrito como "significa que você pode substituir iguais por iguais", mas isso parece ser uma explicação inadequada.
Respostas:
O termo "transparência referencial" vem da filosofia analítica , o ramo da filosofia que analisa construções, declarações e argumentos da linguagem natural com base nos métodos da lógica e da matemática. Em outras palavras, é o assunto mais próximo fora da ciência da computação do que chamamos de semântica da linguagem de programação . O filósofo Willard Quine foi responsável por iniciar o conceito de transparência referencial, mas também estava implícito nas abordagens de Bertrand Russell e Alfred Whitehead.
Na sua essência, "transparência referencial" é uma idéia muito simples e clara. O termo "referente" é usado na filosofia analítica para falar sobre o que uma expressão se refere . É aproximadamente o mesmo que queremos dizer com "significado" ou "denotação" na semântica da linguagem de programação. Usando o exemplo de Andrew Birkett ( postagem no blog ), o termo "capital da Escócia" se refere à cidade de Edimburgo. Esse é um exemplo direto de um "referente".
Um contexto em uma frase é "referencialmente transparente" se a substituição de um termo nesse contexto por outro termo que se refere à mesma entidade não altera o significado. Por exemplo
O Parlamento escocês se reúne na capital da Escócia.
significa o mesmo que
O Parlamento escocês se reúne em Edimburgo.
Portanto, o contexto "O Parlamento escocês se reúne em ..." é um contexto referencialmente transparente. Podemos substituir "a capital da Escócia" por "Edimburgo" sem alterar o significado. Em outras palavras, o contexto se preocupa apenas com o que o termo se refere e nada mais. É nesse sentido que o contexto é "referencialmente transparente".
Por outro lado, na frase,
Edimburgo é a capital da Escócia desde 1999.
não podemos fazer essa substituição. Se o fizéssemos, obteríamos "Edimburgo é Edimburgo desde 1999", o que é uma coisa louca de se dizer, e não transmite o mesmo significado que a frase original. Assim, parece que o contexto "Edimburgo tem sido ... desde 1999" é referencialmente opaco (o oposto de referencialmente transparente). Aparentemente, ele se preocupa com algo mais do que aquilo a que o termo se refere. O que é isso?
Coisas como "a capital da Escócia" são chamadas de termos definidos e não causam muita dor de cabeça aos lógicos e filósofos por um longo tempo. Russell e Quine os classificaram dizendo que eles não são realmente "referenciais", ou seja, é um erro pensar que os exemplos acima são usados para se referir a entidades. A maneira correta de entender "Edimburgo é a capital da Escócia desde 1999" é dizer
A Escócia possui uma capital desde 1999 e essa capital é Edimburgo.
Esta frase não pode ser transformada em uma noz. Problema resolvido! O objetivo de Quine era dizer que a linguagem natural é confusa, ou pelo menos complicada, porque é conveniente para uso prático, mas filósofos e lógicos devem trazer clareza ao entendê-las da maneira correta. A transparência referencial é uma ferramenta a ser usada para trazer tanta clareza de significado .
O que tudo isso tem a ver com programação? Na verdade não muito. Como dissemos, a transparência referencial é uma ferramenta a ser usada na compreensão da linguagem, ou seja, na atribuição de significado . Christopher Strachey , que fundou o campo da semântica da linguagem de programação, o usou em seu estudo do significado. Seu artigo fundamental " Conceitos fundamentais em linguagens de programação " está disponível na web. É um jornal bonito e todos podem ler e entender. Então faça isso. Você será muito esclarecido. Ele introduz o termo "transparência referencial" neste parágrafo:
Uma das propriedades mais úteis das expressões é aquela chamada pela transparência referencial do Quine. Em essência, isso significa que, se desejamos encontrar o valor de uma expressão que contenha uma subexpressão, a única coisa que precisamos saber sobre a subexpressão é seu valor. Quaisquer outras características da subexpressão, como sua estrutura interna, o número e a natureza de seus componentes, a ordem em que são avaliadas ou a cor da tinta em que são escritas, são irrelevantes para o valor da principal expressão.
O uso de "em essência" sugere que Strachey está parafraseando-o para explicá-lo em termos simples. Programadores funcionais parecem entender este parágrafo à sua maneira. Existem 9 outras ocorrências de "transparência referencial" no artigo, mas elas não parecem se preocupar com nenhuma das outras. De fato, todo o artigo de Strachey é dedicado a explicar o significado de linguagens de programação imperativas . Hoje, porém, programadores funcionais afirmam que linguagens de programação imperativas não são referencialmente transparentes. Strachey estaria revirando seu túmulo.
Nós podemos salvar a situação. Dissemos que a linguagem natural é "confusa ou pelo menos complicada" porque é feita para ser conveniente para uso prático. Linguagens de programação são da mesma maneira. Eles são "confusos ou pelo menos complicados" porque são feitos para serem convenientes para uso prático. Isso não significa que eles precisam nos confundir. Eles apenas precisam ser entendidos da maneira certa, usando uma meta linguagem que é referencialmente transparente para que tenhamos clareza de significado. No artigo que citei, Strachey faz exatamente isso. Ele explica o significado de linguagens de programação imperativas, dividindo-as em conceitos elementares, nunca perdendo a clareza em nenhum lugar. Uma parte importante de sua análise é apontar que expressões em linguagens de programação têm dois tipos de "valores",valores r . Antes do artigo de Strachey, isso não era compreendido e a confusão reinava suprema. Hoje, a definição de C menciona-a rotineiramente e todo programador de C entende a distinção. (É difícil dizer se os programadores de outras línguas o entendem da mesma forma.
Quine e Strachey estavam preocupados com o significado das construções de linguagem que envolvem alguma forma de dependência de contexto. Por exemplo, nosso exemplo "Edimburgo é a capital da Escócia desde 1999" significa o fato de que "capital da Escócia" depende do horário em que está sendo considerada. Essa dependência de contexto é uma realidade, tanto em linguagens naturais quanto em linguagens de programação. Mesmo na programação funcional, variáveis livres e vinculadas devem ser interpretadas com relação ao contexto em que aparecem. A dependência de qualquer tipo de contexto bloqueia a transparência referencial de uma maneira ou de outra. Se você tentar entender o significado dos termos sem considerar os contextos dos quais eles dependem, você acabará novamente com confusão. Quine estava preocupado com o significado da lógica modal. Ele sustentou issoa lógica modal era referencialmente opaca e deveria ser limpa, traduzindo-a em uma estrutura referencialmente transparente (por exemplo, considerando a necessidade como a disponibilidade). Ele perdeu em grande parte esse debate. Tanto lógicos quanto filósofos consideraram perfeitamente possível a semântica mundial de Kripke. Situação semelhante também reina com a programação imperativa. A dependência de estado explicada por Strachey e a dependência de loja explicada por Reynolds (de maneira semelhante à possível semântica mundial de Kripke) são perfeitamente adequadas. Programadores funcionais não conhecem muito desta pesquisa. Suas idéias sobre transparência referencial devem ser tomadas com um grande grão de sal.
[Nota adicional: Os exemplos acima ilustram que uma frase simples como "capital da Escócia" tem vários níveis de significado. Em um nível, podemos estar falando sobre a capital no momento atual. Em outro nível, poderíamos falar sobre todas as capitais possíveis que a Escócia poderia ter ao longo do tempo. Podemos "ampliar" um contexto específico e "diminuir o zoom" para abranger todos os contextos com bastante facilidade na prática normal. A eficiência da linguagem natural faz uso de nossa capacidade de fazê-lo. Linguagens de programação imperativas são eficientes da mesma maneira. Podemos usar uma variável x no lado direito de uma atribuição (o valor r ) para falar sobre seu valor em um estado específico. Ou, podemos falar sobre seu valor lque abrange todos os estados. As pessoas raramente ficam confusas com essas coisas. No entanto, eles podem ou não ser capazes de explicar com precisão todas as camadas de significado inerentes às construções da linguagem. Todas essas camadas de significado não são necessariamente "óbvias" e é uma questão de ciência estudá-las adequadamente. No entanto, a inarticulação das pessoas comuns para explicar esses significados em camadas não implica que eles estejam confusos sobre eles.]
Um "pós-escrito" separado abaixo relaciona essa discussão às preocupações da programação funcional e imperativa .
Transparência referencial, um termo comumente usado em programação funcional, significa que, dada uma função e um valor de entrada, você sempre receberá a mesma saída. Ou seja, não há estado externo usado na função.
Aqui está um exemplo de uma função transparente referencial:
int plusOne(int x)
{
return x+1;
}
Com uma função transparente referencial, dada uma entrada e uma função, você pode substituí-lo por um valor em vez de chamar a função. Portanto, em vez de chamar plusOne com um parâmetro 5, podemos substituí-lo por 6.
Outro bom exemplo é a matemática em geral. Na matemática, dada uma função e um valor de entrada, ele sempre será mapeado para o mesmo valor de saída. f (x) = x + 1. Portanto, as funções em matemática são referencialmente transparentes.
Esse conceito é importante para os pesquisadores porque significa que, quando você possui uma função referencialmente transparente, ela se presta a paralelização e cache automáticos fáceis.
A transparência referencial é usada sempre em linguagens funcionais como Haskell.
-
Em contraste, existe o conceito de opacidade referencial. Isso significa o contrário. Chamar a função nem sempre produz a mesma saída.
//global G
int G = 10;
int plusG(int x)
{//G can be modified externally returning different values.
return x + G;
}
Outro exemplo é uma função de membro em uma linguagem de programação orientada a objetos. As funções de membro geralmente operam em suas variáveis de membro e, portanto, seriam referenciais opacas. É claro que as funções de membro podem ser referencialmente transparentes.
Ainda outro exemplo é uma função que lê um arquivo de texto e imprime a saída. Esse arquivo de texto externo pode ser alterado a qualquer momento, para que a função seja referencialmente opaca.
Uma função referencialmente transparente é aquela que depende apenas de sua entrada.
[Este é um pós-escrito da minha resposta de 25 de março, em um esforço para aproximar a discussão das preocupações da programação funcional / imperativa.]
A ideia de transparência referencial dos programadores funcionais parece diferir da noção padrão de três maneiras:
Enquanto os filósofos / lógicos usam termos como "referência", "denotação", "designatum" e " bedeutung " (termo alemão de Frege), programadores funcionais usam o termo "valor". (Isso não é tarefa deles. Observo que Landin, Strachey e seus descendentes também usaram o termo "valor" para falar sobre referência / denotação. Pode ser apenas uma simplificação terminológica que Landin e Strachey introduziram, mas parece fazer uma grande diferença quando usado de maneira ingênua.)
Programadores funcionais parecem acreditar que esses "valores" existem dentro da linguagem de programação, não fora. Ao fazer isso, eles diferem dos filósofos e dos semanticistas da linguagem de programação.
Eles parecem acreditar que esses "valores" devem ser obtidos por avaliação.
Por exemplo, o artigo da Wikipedia sobre transparência referencial diz, nesta manhã:
Diz-se que uma expressão é referencialmente transparente se puder ser substituída por seu valor sem alterar o comportamento de um programa (em outras palavras, produzindo um programa que tenha os mesmos efeitos e saída na mesma entrada).
Isso está completamente em desacordo com o que dizem os filósofos / lógicos. Eles dizem que um contexto é referencial ou transparente referencial se uma expressão nesse contexto puder ser substituída por outra expressão que se refira à mesma coisa (uma expressão coreferencial ). Quem são esses filósofos / lógicos? Eles incluem Frege , Russell , Whitehead , Carnap , Quine , Igrejae inúmeros outros. Cada um deles é uma figura imponente. O poder intelectual combinado desses lógicos é impressionante, para dizer o mínimo. Todos eles são unânimes na posição de que referências / denotações existem fora da linguagem formal e expressões dentro da linguagem só podem falar sobre elas. Portanto, tudo o que se pode fazer dentro do idioma é substituir uma expressão por outra expressão que se refere à mesma entidade. Os próprios referentes / denotações não existem no idioma. Por que os programadores funcionais se desviam dessa tradição bem estabelecida?
Pode-se presumir que os semanticistas da linguagem de programação possam tê-los enganado. Mas eles não fizeram.
Landin :
(a) cada expressão tem uma estrutura de subexpressão de aninhamento, (b) cada subexpressão denota algo (geralmente um número, valor de verdade ou função numérica) , (c) o que uma expressão denota, ou seja, seu "valor", depende apenas da valores de suas sub-expressões, não em outras propriedades delas. [Ênfase adicionada]
Stoy :
A única coisa que importa sobre uma expressão é o seu valor, e qualquer subexpressão pode ser substituída por outra igual em valor [ênfase adicionada]. Além disso, o valor de uma expressão é, dentro de certos limites, o mesmo sempre que ocorre ".
o valor de uma expressão depende apenas dos valores de suas expressões constituintes (se houver) e essas subexpressões podem ser substituídas livremente por outras pessoas que possuam o mesmo valor [ênfase adicionada].
Assim, em retrospecto, os esforços de Landin e Strachey para simplificar a terminologia substituindo "referência" / "denotação" por "valor" podem ter sido imprudentes. Assim que alguém ouve um "valor", há uma tentação de pensar em um processo de avaliação que o conduz. É igualmente tentador pensar no que quer que a avaliação produz como o "valor", mesmo que possa ficar bem claro que essa não é a denotação. É o que eu acho que aconteceu com o conceito de "transparência referencial" aos olhos dos programadores funcionais. Mas o "valor" que estava sendo mencionado pelos primeiros semanticistas não é o resultado de uma avaliação ou o resultado de uma função ou coisa parecida. É a denotação do termo.
Depois de entendermos o chamado "valor" de uma expressão ("referência" ou "denotação" no discurso dos filósofos clássicos) como um objeto matemático / conceitual complexo, todos os tipos de possibilidades se abrem.
A relutância dos programadores funcionais em chamar essas linguagens de "referencialmente transparentes" implica apenas que eles são relutantes em admitir objetos matemáticos / conceituais complexos como "valores". Por outro lado, eles parecem perfeitamente dispostos a chamar um transformador de estado de "valor" quando é colocado em sua própria sintaxe favorita e vestido com uma palavra da moda como "mônada". Devo dizer que eles estão sendo totalmente inconsistentes, mesmo que lhes concedamos que sua idéia de "transparência referencial" tenha alguma coerência.
Um pouco de história pode lançar alguma luz sobre como essas confusões surgiram. O período entre 1962 e 1967 foi muito intenso para Christopher Strachey. Entre 1962-65, ele trabalhou em meio período como assistente de pesquisa com Maurice Wilkes para projetar e implementar a linguagem de programação que passou a ser conhecida como CPL. Essa era uma linguagem de programação imperativa, mas deveria ter também recursos poderosos da linguagem de programação funcional. Landin, que era funcionário da Strachey em sua empresa de consultoria, teve uma enorme influência na visão de Strachey das linguagens de programação. No artigo de referência de 1965 " Next 700 linguagens de programação ", Landin promove descaradamente as linguagens de programação funcional (chamando-as de denotativas).linguagens de programação imperativas como sua "antítese". Na discussão que se segue, encontramos Strachey levantando dúvidas sobre a forte posição de Landin.
... DLs formam um subconjunto de todos os idiomas. Eles são um subconjunto interessante, mas inconveniente de usar, a menos que você esteja acostumado. Precisamos deles porque, no momento , não sabemos como construir provas com linguagens que incluem imperativos e saltos. [Ênfase adicionada]
Em 1965, Strachey assumiu a posição de Leitor em Oxford e parece ter trabalhado essencialmente em tempo integral no desenvolvimento de uma teoria de imperativos e saltos. Em 1967, ele estava pronto com uma teoria, ensinada em seu curso sobre " Conceitos fundamentais em linguagens de programação " em uma escola de verão em Copenhague. As anotações das palestras deveriam ter sido publicadas, mas "infelizmente, por causa da edição dilatória, os procedimentos nunca se materializaram; como grande parte do trabalho de Strachey em Oxford, no entanto, o jornal teve uma circulação privada influente". ( Martin Campbell-Kelly )
A dificuldade de obter os escritos de Strachey poderia ter levado à propagação das confusões, com pessoas confiando em fontes secundárias e boatos. Mas agora que os " conceitos fundamentais " estão prontamente disponíveis na Web, não há necessidade de recorrer ao trabalho de adivinhação. Deveríamos ler e decidir o que Strachey queria dizer. Em particular:
Qualquer conversa sobre "transparência referencial" sem entender a distinção entre valores L, valores R e outros objetos complexos que preenchem o universo conceitual do programador imperativo é fundamentalmente equivocada.
Uma função referencialmente transparente é aquela que age como uma função matemática; dadas as mesmas entradas, sempre produzirá as mesmas saídas. Isso implica que o estado passado não é modificado e que a função não possui um estado próprio.
Para aqueles que precisam de uma explicação concisa, arriscarei uma (mas leia a divulgação abaixo).
A transparência referencial em uma linguagem de programação promove o raciocínio equacional - quanto mais transparência referencial você tiver, mais fácil será o raciocínio equacional. Por exemplo, com uma definição de função (pseudo),
fx = x + x,
a facilidade com que você pode (com segurança) substituir f (foo) por foo + foo no escopo desta definição, sem ter muitas restrições sobre onde é possível executar essa redução, é uma boa indicação de quanta transparência referencial sua linguagem de programação tem.
Por exemplo, se foo fosse x ++ no sentido de programação C, não seria possível executar essa redução com segurança (ou seja, se você realizasse essa redução, não terminaria com o mesmo programa em que iniciou).
Em linguagens de programação práticas, você não verá perfeita transparência referencial, mas os programadores funcionais se importam com isso mais do que a maioria (cf. Haskell, onde é um objetivo central).
(Divulgação completa: sou um programador funcional, portanto, pela resposta principal, você deve tomar essa explicação com um pouco de sal.)
Se você está interessado na etimologia (por exemplo, por que esse conceito tem esse nome específico), dê uma olhada no meu blog sobre o assunto. A terminologia vem do filósofo / lógico Quine.
Em 1, há uma clareza de dois idiomas em questão:
Em 2, graças à proximidade do objeto e das metalinguagens, eles podem ser confundidos.
Como implementador de linguagem, acho que preciso lembrar constantemente dessa distinção.
Reddy, posso parafrasear você assim :-)
Nos contextos de programação funcional e semântica, o termo Transparência Referencial não é referencialmente transparente.
Espero que a seguinte resposta adicione e qualifique as controversas 1ª e 3ª respostas.
Vamos admitir que uma expressão denota ou se refere a algum referente. No entanto, uma pergunta é se esses referentes podem ser codificados isomorficamente como parte das próprias expressões, chamando essas expressões de "valores". Por exemplo, valores numéricos literais são um subconjunto do conjunto de expressões aritméticas, valores verdadeiros são um subconjunto do conjunto de expressões booleanas, etc. A idéia é avaliar uma expressão em seu valor (se houver). Portanto, a palavra 'valor' pode se referir a uma denotação ou a um elemento distinto do conjunto de expressões. Mas se houver um isomorfismo (uma bijeção) entre o referente e o valor, podemos dizer que eles são a mesma coisa. (Dito isto, é preciso ter cuidado ao definir os referentes e o isomorfismo, conforme comprovado pelo campo da semântica denotacional. Para colocar um exemplo mencionado pelas respostas à 3ª resposta,data Nat = Zero | Suc Nat
não corresponde conforme o esperado ao conjunto de números naturais.)
Vamos escrever E[·]
para uma expressão com um buraco, também conhecido em alguns setores como um 'contexto'. Dois exemplos de contexto para expressões do tipo C são [·]+1
e
[·]++
.
Vamos escrever [[·]]
para a função que pega uma expressão (sem buraco) e entrega seu significado (referente, denotação etc.) em algum universo que fornece significado. (Estou emprestando uma notação do campo da semântica denotacional.)
Vamos adaptar formalmente a definição de Quine da seguinte forma: um contexto E[·]
é referencialmente transparente se houver duas expressões E1
e E2
(não existem buracos) de tal modo que [[E1]] = [[E2]]
(ou seja, as expressões denotem / se refiram ao mesmo referente), então é o caso que [[E[E1]]] = [[E[E2]]]
(ou seja, preencher -no buraco com um E1
ou E2
resulta em expressões que também denotam o mesmo referente).
A regra de Leibniz de substituir iguais por iguais é normalmente expressa como 'se E1 = E2
assim for
E[E1] = E[E2]
', o que diz que E[·]
é uma função. Uma função (ou, nesse caso, um programa que calcula a função) é um mapeamento de uma origem para um destino, para que haja no máximo um elemento de destino para cada elemento de origem. Funções não determinísticas são desnômeros, são relações, funções que fornecem conjuntos, etc. Se na regra de Leibniz a igualdade =
é denotacional, os colchetes duplos são simplesmente tomados como garantidos e elididos. Portanto, um contexto referencialmente transparente é uma função. E a regra de Leibniz é o principal ingrediente do raciocínio equacional; portanto, o raciocínio equacional está definitivamente relacionado à transparência referencial.
Embora [[·]]
seja uma função de expressões para denotações, pode ser uma função de expressões para 'valores' entendidos como um subconjunto restrito de expressões e [[·]]
pode ser entendida como avaliação.
Agora, se E1
é uma expressão e E2
é um valor, temos o que eu acho que significa a maioria das pessoas ao definir transparência referencial em termos de expressões, valores e avaliação. Mas, como ilustrado pelas 1ª e 3ª respostas desta página, esta é uma definição imprecisa.
O problema com contextos como [·]++
não é o efeito colateral, mas seu valor não é definido em C isomorficamente ao seu significado. Funções não são valores (bem, ponteiros para funções são), enquanto nas linguagens de programação funcionais são. Landin, Strachey e os pioneiros da semântica denotacional foram bastante inteligentes ao usar mundos funcionais para fornecer significado.
Por imperativo C-como línguas nós podemos (aproximadamente) fornecer semântica de expressões usando a função [[·]] : Expression -> (State -> State x Value)
.
Value
é um subconjunto de Expression
. State
contém pares (identificador, valor). A função semântica pega uma expressão e entrega como significado uma função do estado atual para o par com o estado atualizado e um valor. Por exemplo, [[x]]
é a função do estado atual para o par cujo primeiro componente é o estado atual e cujo segundo componente é o valor de x. Por outro lado, [[x++]]
é a função do estado atual para o par cujo primeiro componente é um estado no qual o valor de x é incrementado e cujo segundo componente é esse mesmo valor. Nesse sentido, o contexto [·]++
é referencialmente transparente se satisfizer a definição dada acima.
Acho que os programadores funcionais têm o direito de usar a transparência referencial no sentido de que se recuperam naturalmente [[·]]
como uma função das expressões aos valores. Funções são valores de primeira classe e o estado também pode ser um valor, não uma denotação. A mônada do estado é (em parte) um mecanismo limpo para passar (ou encadear) o estado.
Observe que esse conceito de "significado" é algo que acontece na mente do observador. Assim, a mesma "referência" pode significar coisas diferentes para pessoas diferentes. Por exemplo, temos uma página de desambiguação de Edimburgo na Wikipedia.
Uma questão relacionada que pode aparecer no contexto da programação pode ser o polimorfismo.
E talvez devêssemos ter um nome para o caso especial de polimorfismo (ou talvez até vazamento), onde, para nossos propósitos, os diferentes casos polimórficos são semanticamente equivalentes (em vez de apenas serem semelhantes. Por exemplo, o número 1 - que pode ser representado usando um tipo inteiro, complexo ou qualquer outro tipo - pode ser tratado polimorficamente).
Achei a definição de transparência referencial no livro " Estrutura e implementação de programas de computador " (o Livro do assistente) útil porque é complementada por uma explicação de como a transparência referencial é violada pela introdução da operação de atribuição . Confira o seguinte slide deck que fiz sobre o assunto: https://www.slideshare.net/pjschwarz/introducing-assignment-invalidates-the-substitution-model-of-evaluation-and-violates-referential-transparency-as- explicado-em-sicp-o-assistente-livro
A transparência referencial pode ser simplesmente declarada como:
Por exemplo, a linguagem de programação Haskell é uma linguagem funcional pura; o que significa que é referencialmente transparente.