Prima di affrontare l'identità, definiamo ciò che intendiamo per uguaglianza un po 'più precisamente. Diciamo che due cose sono uguali se e solo se non possiamo distinguerle (vedi: Identity of indiscernibles ). Ciò significa che se due cose sono uguali o meno dipende dai mezzi che dobbiamo ispezionare.
Pensiamo ancora un po 'in termini di programmazione. Lasciamo i nostri preconcetti alla porta e supponiamo di lavorare in un linguaggio nuovo di zecca in cui tutte le variabili e i valori sono immutabili. Con la definizione di cui sopra, due valori A
e B
sono uguali se e solo se non ci sono programmi nella lingua che producono risultati diversi quando A
è usato al posto di B
o viceversa. Diciamo che A
e B
sono (IEEE 754) float e quando viene sostituito nell'espressione _ + 1.0
, il risultato è 1.0
per entrambi A
e B
. Sicuramente A
e B
sono entrambi zero. Sono uguali? Dipende - la lingua fornisce qualsiasi funzione che mi permetta di determinare il segno dello zero ? Se non lo fa, sono uguali; se lo fa, potrebbe non esserlo.
Quindi due valori sono uguali ogni volta che danno gli stessi risultati per tutte le possibili combinazioni di operazioni che supportano. I valori immutabili in particolare non producono risultati diversi a seconda di quali operazioni sono state precedentemente applicate a loro. Per questo motivo, non ci interessa se due variabili puntano a due copie dello stesso valore o se entrambe puntano alla stessa copia.
Che cosa ha a che fare con la mutabilità? La mutabilità implica che il nostro linguaggio abbia qualche nozione di una cella di memoria il cui contenuto può essere sovrascritto. Supponiamo che aggiungiamo il supporto per le celle di memoria mutabili nella nostra lingua:
-
ref <value>
crea una nuova cella di memoria, distinta da tutte le altre, inizializzata su <value>
.
-
<variable> := <value>
sovrascrive il contenuto di una cella di riferimento.
-
!<variable>
restituisce il valore attualmente memorizzato in una cella di riferimento.
Ora pensiamo a cosa significa l'uguaglianza per le celle di memoria. Supponi A = ref 0
e B = A
. Considera questo programma:
A := 1
print(!_)
Sostituendo il bianco per A
stampa 1
e sostituendo B
stampa 1
. Supponiamo ora A = ref 0
e B = ref 0
. In questo caso, la sostituzione con il programma precedente stampa 1
e 0
, poiché ora A
e B
puntano a celle di memoria distinte.
Quindi è importante per noi se due riferimenti puntino alla stessa cella di memoria oa celle di memoria diverse. Dal momento che ciò conta, sarebbe utile avere un modo efficace e generale di distinguere due riferimenti. Il nostro metodo attuale per confrontare i valori che detengono, e se sono uguali, uno di loro è problematico per una serie di ragioni:
- Dipende dall'essere in grado di confrontare i valori memorizzati nelle celle di memoria per l'uguaglianza. L'uguaglianza non ha senso per tutti i tipi - per esempio, generalmente non ha senso per le funzioni, perché non esiste un metodo generale per determinare se due funzioni sconosciute sono uguali (questo si sta avventurando nel territorio di Problema di Arresto). Quindi, dati due riferimenti alle celle di memoria che memorizzano funzioni, non possiamo confrontare le funzioni che mantengono per l'uguaglianza.
- Dipende dall'avere un valore che possiamo assegnare a uno dei due riferimenti. Quindi, anche se l'uguaglianza avesse senso per tutti i tipi nella lingua, abbiamo ancora bisogno di accedere a un valore per ogni tipo che vogliamo confrontare. Cosa succede se la costruzione di un valore di quel tipo ha effetti collaterali?
- Il valore di riferimento che usiamo per mutare uno dei riferimenti deve essere diverso dal valore che ha già la cella di memoria, quindi abbiamo effettivamente bisogno di due valori.
- Il codice per confrontare i riferimenti di tipi diversi avrà esattamente lo stesso valore per i due valori che usiamo.
- Abbiamo bisogno di eseguire il backup e ripristinare il valore del riferimento che mutiamo per evitare di cambiare il significato del programma.
Quindi sarebbe utile che il linguaggio fornisse un'operazione per controllare direttamente se due riferimenti puntano alla stessa cella di memoria mutevole. Tale funzione è inutile per valori immutabili; in effetti, direi che è decisamente dannoso. Se esistesse un modo per dire se due 1
s sono memorizzati in posti diversi nella memoria, allora ci possono essere programmi che si preoccupano se io passo a una 1
o l'altra. Non voglio davvero preoccuparmi se ho "il giusto 1
"; la matematica è già abbastanza difficile! Quindi è chiaro che essere in grado di verificare l'uguaglianza della memoria è utile soprattutto per i tipi mutabili.