Basandosi sulla risposta di Charles, la principale difficoltà nella teoria dei linguaggi di programmazione è che la nozione naturale di equivalenza dei programmi non è in genere una rigida uguaglianza né nella semantica matematica più semplice che si possa dare, o nel modello macchina sottostante. Ad esempio, considera il seguente bit di codice simile a Java:
Object x = new Object();
Object y = new Object();
... some more code ...
Quindi questo programma crea un oggetto e lo denomina x, quindi crea un secondo oggetto chiamato y, quindi continua l'esecuzione di altro codice. Supponiamo ora che un programmatore decida di invertire l'ordine di allocazione di questi due oggetti:
Object y = new Object();
Object x = new Object();
... some more code ...
Ora, fai la domanda: questo refactoring cambia il comportamento del programma? Da un lato, sulla macchina sottostante, xey verranno allocati in posizioni diverse nelle due esecuzioni del programma. Quindi, in questo senso, il programma si comporta diversamente.
Ma in un linguaggio simile a Java, puoi solo testare i riferimenti per l'uguaglianza e non per l'ordine, quindi questa è una differenza che "un po 'di codice in più" non può osservare . Di conseguenza, la maggior parte dei programmatori si aspetta che l'inversione dell'ordine non faccia alcuna differenza per la risposta finale e la maggior parte degli autori di compilatori si aspettano di essere in grado di eseguire riordini e ottimizzazioni su questa base. (D'altra parte, in un C-come il linguaggio, è possibile confrontare i puntatori per l'ordinazione, gettandoli in interi prima, e così questo riordino non non necessariamente conservare comportamento osservabile.)
Una delle domande centrali della semantica è rispondere alla domanda su quando due programmi sono notevolmente equivalenti. Poiché la nostra nozione di osservazione dipende dalle caratteristiche del linguaggio di programmazione, finiamo con una definizione come "due programmi sono equivalenti quando nessun programma client può calcolare risposte diverse in base alla ricezione di tali programmi come input". La quantificazione su tutti i programmi client è ciò che rende difficile questa domanda - sembra che tu debba finire per dire qualcosa su tutti i possibili programmi client per dire qualcosa su due particolari pezzi di codice.
Il trucco con la semantica denotazionale è quello di dare un'interpretazione matematica che ti consenta di evitare questa quantificazione universale - dici che il significato di un pezzo di codice è un valore matematico e li confronti confrontando per vedere se sono matematicamente uguali o non. Questo è locale (cioè composizionale) e non comporta la quantificazione su tutti i possibili clienti. (Devi mostrare che la semantica denotazionale implica l'equivalenza contestuale perché sia sana, ovviamente. Quando è completa - quando l'uguaglianza denotazionale è esattamente la stessa dell'equivalenza contestuale, diciamo che la semantica è "completamente astratta".)
Ma significa che è necessario assicurarsi che la semantica denotazionale convalidi tali equivalenze. Quindi, per questo esempio, se si desidera fornire una semantica denotazionale per questo linguaggio simile a Java, è necessario assicurarsi non solo che chiamare new prenda un heap e restituisca un nuovo heap con l'oggetto appena creato, ma che il significato del programma è invariante uguale in tutte le permutazioni dell'heap di input. Ciò può comportare strutture matematiche piuttosto complesse (ad esempio, in questo caso lavorare in una categoria che garantisce che tutto funzioni in un gruppo di permutazione adatto).