Antes de abordar a identidade, vamos definir o que entendemos por igualdade um pouco mais precisamente. Dizemos que duas coisas são iguais se, e somente se, não podemos diferenciá-las (ver: Identidade dos indiscerníveis ). Isso significa que se duas coisas são iguais ou não, depende dos meios que temos para inspecioná-las.
Vamos pensar um pouco mais sobre isso em termos de programação. Vamos deixar nossos preconceitos à porta e supor que estamos trabalhando em uma linguagem desconhecida totalmente nova, onde todas as variáveis e valores são imutáveis. Pela definição de cima, dois valores A
e B
são iguais se e apenas se existem programas de NO na língua que produzem resultados diferentes quando A
é usado em lugar de B
, ou vice-versa. Digamos A
e B
somos (IEEE 754) flutuadores, e quando substituído na expressão _ + 1.0
, o resultado é 1.0
para ambos A
e B
. Certamente A
e B
são ambos zero. Eles são iguais? Isso depende - a linguagem fornece alguma função que me permita determinar o sinal do zero? Se não, eles são iguais; se isso acontecer, eles podem não estar.
Portanto, dois valores são iguais sempre que fornecem os mesmos resultados para todas as combinações possíveis de operações suportadas. Valores imutáveis, em particular, não produzem resultados diferentes, dependendo de quais operações foram aplicadas anteriormente a eles. Por esse motivo, não nos importamos se duas variáveis apontam para duas cópias do mesmo valor ou se ambas apontam para a mesma cópia.
O que isso tem a ver com mutabilidade? Mutabilidade implica que nossa linguagem tenha alguma noção de uma célula de memória cujo conteúdo possa ser substituído. Digamos que adicionemos suporte a células de memória mutáveis ao nosso idioma:
ref <value>
cria uma nova célula de memória, diferente de todas as outras, inicializada em <value>
.
<variable> := <value>
substitui o conteúdo de uma célula de referência.
!<variable>
retorna o valor atualmente armazenado em uma célula de referência.
Agora vamos pensar sobre o que igualdade significa para células de memória. Suponha A = ref 0
e B = A
. Considere este programa:
A := 1
print(!_)
Substituindo A
impressões em branco 1
e substituindo por B
impressões 1
também. Agora suponha A = ref 0
e B = ref 0
. Nesse caso, a substituição no programa acima será impressa 1
e 0
, desde agora, A
e B
apontará para células de memória distintas.
Portanto, importa para nós se duas referências apontam para a mesma célula de memória ou células de memória diferentes. Como isso importa, seria útil ter uma maneira eficiente e geral de distinguir duas referências. Nosso método atual de comparar os valores que eles mantêm e, se eles são iguais a uma mutação, é problemático por vários motivos:
- Depende da capacidade de comparar os valores armazenados nas células de memória para obter igualdade. Igualdade não faz sentido para todos os tipos - por exemplo, geralmente não faz sentido para funções, porque não existe um método geral para determinar se duas funções desconhecidas são iguais (isso está se aventurando no território do Problema de Colocação). Portanto, dadas duas referências às células de memória que armazenam funções, não podemos comparar as funções que elas possuem para a igualdade.
- Depende de ter algum valor que possamos atribuir a uma das duas referências. Portanto, mesmo que a igualdade faça sentido para todos os tipos no idioma, ainda precisamos acessar um valor para cada tipo que queremos comparar. E se a construção de um valor desse tipo tiver efeitos colaterais?
- O valor de referência que usamos para alterar uma das referências deve ser diferente do valor que a célula de memória já possui; portanto, precisamos de dois valores.
- O código para comparar referências de tipos diferentes será exatamente o mesmo, exceto pelos dois valores que usamos.
- Precisamos fazer backup e restaurar o valor da referência que modificamos para evitar alterar o significado do programa.
Portanto, seria útil para o idioma fornecer uma operação para verificar diretamente se duas referências apontam para a mesma célula de memória mutável. Essa função é inútil para valores imutáveis; na verdade, eu diria que é absolutamente prejudicial. Se existe uma maneira de saber se dois 1
s estão armazenados em lugares diferentes da memória, pode haver programas que se importam se eu passo um 1
ou outro. Eu realmente não quero me preocupar se tenho "o certo 1
"; a matemática já é difícil o suficiente! Portanto, fica claro que poder verificar a igualdade de memória é útil principalmente para tipos mutáveis.