Antes de abordar la identidad, definamos lo que entendemos por igualdad un poco más precisamente. Decimos que dos cosas son iguales si y solo si no podemos distinguirlas (ver: Identidad de indiscernibles ). Eso significa que si dos cosas son iguales o no depende de los medios que tengamos para inspeccionarlas.
Pensemos en eso un poco más en términos de programación. Dejemos nuestras ideas preconcebidas en la puerta y supongamos que estamos trabajando en un nuevo lenguaje desconocido donde todas las variables y valores son inmutables. Según la definición anterior, dos valores A
y B
son iguales si y solo si NO hay programas en el lenguaje que produzcan resultados diferentes cuando A
se usan en lugar de B
o viceversa. Digamos A
y B
son flotantes (IEEE 754), y cuando se sustituyen en la expresión _ + 1.0
, el resultado es 1.0
para ambos A
y B
. Seguramente A
y B
ambos son cero. ¿Son iguales? Eso depende: ¿el lenguaje proporciona alguna función que me permita determinar el signo del cero?? Si no es así, son iguales; si lo hace, pueden no serlo.
Por lo tanto, dos valores son iguales cada vez que dan los mismos resultados para todas las combinaciones posibles de operaciones que admiten. Los valores inmutables en particular no producen resultados diferentes dependiendo de las operaciones que se les aplicaron previamente. Por esa razón, no nos importa si dos variables apuntan a dos copias del mismo valor o si ambas apuntan a la misma copia.
¿Qué tiene esto que ver con la mutabilidad? La mutabilidad implica que nuestro lenguaje tiene una noción de una celda de memoria cuyos contenidos pueden sobrescribirse. Digamos que agregamos soporte para celdas de memoria mutables a nuestro idioma:
ref <value>
crea una nueva celda de memoria, distinta de todas las demás, inicializada en <value>
.
<variable> := <value>
sobrescribe el contenido de una celda de referencia.
!<variable>
devuelve el valor actualmente almacenado en una celda de referencia.
Ahora pensemos en lo que significa la igualdad para las celdas de memoria. Supongamos A = ref 0
y B = A
. Considere este programa:
A := 1
print(!_)
Sustituyendo las A
impresiones en blanco 1
y sustituyendo también por las B
impresiones 1
. Ahora supongamos A = ref 0
y B = ref 0
. En este caso, sustituyendo en el programa imprime anteriores 1
y 0
, desde ahora A
y B
punto a distintas celdas de memoria.
Por lo tanto, nos importa si dos referencias apuntan a la misma celda de memoria o diferentes celdas de memoria. Como eso es importante, sería útil tener una forma eficiente y general de distinguir dos referencias. Nuestro método actual de comparar los valores que tienen, y si son iguales, mutar uno de ellos es problemático por varias razones:
- Depende de poder comparar los valores almacenados en las celdas de memoria para la igualdad. La igualdad no tiene sentido para todos los tipos; por ejemplo, generalmente no tiene sentido para las funciones, porque no hay un método general para determinar si dos funciones desconocidas son iguales (esto es aventurarse en el territorio del Problema de detención). Entonces, dadas dos referencias a las celdas de memoria que almacenan funciones, no podemos comparar las funciones que tienen para la igualdad.
- Depende de tener algún valor que podamos asignar a una de las dos referencias. Entonces, incluso si la igualdad tiene sentido para todos los tipos en el idioma, todavía necesitamos acceso a un valor para cada tipo que queremos comparar. ¿Qué pasa si construir un valor de ese tipo tiene efectos secundarios?
- El valor de referencia que usamos para mutar una de las referencias debe ser diferente del valor que la celda de memoria ya tiene, por lo que en realidad necesitamos dos valores.
- El código para comparar referencias de diferentes tipos se verá exactamente igual, salvo para los dos valores que usamos.
- Necesitamos respaldar y restaurar el valor de la referencia que mutamos para evitar cambiar el significado del programa.
Por lo tanto, sería útil que el lenguaje proporcione una operación para verificar directamente si dos referencias apuntan a la misma celda de memoria mutable. Tal función no tiene sentido para valores inmutables; de hecho, diría que es francamente dañino. Si existía una forma de saber si dos 1
s están almacenados en diferentes lugares en la memoria, entonces puede haber programas que se preocupen si paso uno 1
u otro. Realmente no quiero preocuparme si tengo "el derecho 1
"; ¡las matemáticas ya son bastante difíciles! Por lo tanto, está claro que poder verificar la igualdad de memoria es principalmente útil para los tipos mutables.