Com base na resposta de Charles, a principal dificuldade na teoria das linguagens de programação é que a noção natural de equivalência de programas geralmente não é uma igualdade estrita, nem na semântica matemática mais direta que você pode fornecer, nem no modelo de máquina subjacente. Por exemplo, considere o seguinte bit de código semelhante ao Java:
Object x = new Object();
Object y = new Object();
... some more code ...
Portanto, este programa cria um objeto e o nomeia x, depois cria um segundo objeto chamado y e continua executando mais um código. Agora, suponha que um programador decida inverter a ordem de alocação desses dois objetos:
Object y = new Object();
Object x = new Object();
... some more code ...
Agora, faça a pergunta: essa refatoração muda o comportamento do programa? Por um lado, na máquina subjacente, x e y serão alocados em locais diferentes nas duas execuções do programa. Portanto, nesse sentido, o programa se comporta de maneira diferente.
Porém, em uma linguagem semelhante a Java, você só pode testar referências de igualdade e não de ordem, portanto, essa é uma diferença que o "um pouco mais de código" não pode observar . Como resultado, a maioria dos programadores espera que a reversão da ordem não faça diferença para a resposta final, e a maioria dos criadores de compiladores espera poder realizar reordenamentos e otimizações com base nisso. (Por outro lado, em uma linguagem C, você pode comparar indicadores para pedidos, convertendo-os em números inteiros primeiro e, portanto, essa reordenação não preserva necessariamente o comportamento observável.)
Uma das questões centrais da semântica é responder à questão de quando dois programas são notavelmente equivalentes. Como nossa noção de observação depende dos recursos da linguagem de programação, terminamos com uma definição como "dois programas são equivalentes quando nenhum programa cliente pode calcular respostas diferentes com base no recebimento desses programas como entradas". A quantificação de todos os programas clientes é o que dificulta essa pergunta - parece que você acaba dizendo algo sobre todos os programas possíveis para dizer algo sobre dois trechos de código específicos.
O truque da semântica denotacional é fornecer uma interpretação matemática que permita evitar essa quantificação universal - você diz que o significado de um pedaço de código é algum valor matemático e as compara verificando se são matematicamente iguais ou não. É local (isto é, composicional) e não envolve quantificação sobre todos os possíveis clientes. (Você precisa mostrar que a semântica denotacional implica equivalência contextual para que seja correta, é claro. Quando está completa - quando a igualdade denotacional é exatamente igual à equivalência contextual, dizemos que a semântica é "totalmente abstrata".)
Mas significa que você precisa garantir que a semântica denotacional valide essas equivalências. Portanto, para este exemplo, se você deseja fornecer uma semântica denotacional para essa linguagem semelhante a Java, é necessário garantir não apenas que chamar new pegue um monte e devolva um novo heap com o objeto recém-criado, mas que o significado do programa é invariável mesmo em todas as permutações do heap de entrada. Isso pode envolver estruturas matemáticas bastante complexas (por exemplo, nesse caso, trabalhar em uma categoria que garante que tudo funcione com um grupo de permutação adequado).