En s'appuyant sur la réponse de Charles, la principale difficulté de la théorie des langages de programmation est que la notion naturelle d'équivalence des programmes n'est généralement pas une stricte égalité, ni dans la sémantique mathématique la plus simple que vous pouvez donner, ni dans le modèle de machine sous-jacent. Par exemple, considérez le bit suivant de code de type Java:
Object x = new Object();
Object y = new Object();
... some more code ...
Donc, ce programme crée un objet et le nomme x, puis crée un deuxième objet nommé y, puis continue d'exécuter un peu plus de code. Supposons maintenant qu'un programmeur décide de retourner l'ordre d'allocation de ces deux objets:
Object y = new Object();
Object x = new Object();
... some more code ...
Maintenant, posez la question: cette refactorisation change-t-elle le comportement du programme? D'une part, sur la machine sous-jacente, x et y seront alloués à des emplacements différents dans les deux exécutions du programme. Donc, dans ce sens, le programme se comporte différemment.
Mais dans un langage de type Java, vous ne pouvez tester les références que pour leur égalité, et non pour leur ordre, c'est donc une différence que le «code plus» ne peut pas observer . Par conséquent, la plupart des programmeurs s'attendront à ce que l'annulation de l'ordre ne change rien à la réponse finale, et la plupart des rédacteurs du compilateur s'attendent à pouvoir effectuer des réorganisations et des optimisations sur cette base. (D'autre part, dans un C-comme la langue, vous pouvez comparer les pointeurs pour la commande, en les jetant aux entiers d' abord, et donc ce ne réordonnancement pas préserve nécessairement le comportement observable.)
L'une des questions centrales de la sémantique est de répondre à la question de savoir quand deux programmes sont observablement équivalents. Puisque notre notion d'observation dépend des caractéristiques du langage de programmation, nous nous retrouvons avec une définition comme «deux programmes sont équivalents quand aucun programme client ne peut calculer des réponses différentes en fonction de la réception de ces programmes en entrée». La quantification sur tous les programmes clients est ce qui rend cette question difficile - il semble que vous ayez à dire quelque chose sur tous les programmes clients possibles pour dire quelque chose sur deux morceaux de code particuliers.
L'astuce avec la sémantique dénotationnelle est de donner une interprétation mathématique qui vous permet d'éviter cette quantification universelle - vous dites que la signification d'un morceau de code est une valeur mathématique, et vous les comparez en vérifiant si elles sont mathématiquement égales ou ne pas. Ceci est local (c'est-à-dire compositionnel) et n'implique pas de quantification sur tous les clients possibles. (Vous devez montrer que la sémantique dénotationnelle implique une équivalence contextuelle pour qu'elle soit saine, bien sûr. Quand elle est complète - lorsque l'égalité dénotationnelle est exactement la même que l'équivalence contextuelle, nous disons que la sémantique est "entièrement abstraite".)
Mais cela signifie que vous devez vous assurer que la sémantique dénotationnelle valide ces équivalences. Donc, pour cet exemple, si vous vouliez donner une sémantique dénotationnelle pour ce langage de type Java, vous devez vous assurer non seulement que l'appel de new prend un tas et vous donne un nouveau tas avec l'objet nouvellement créé, mais que la signification du programme est invariant même sous toutes les permutations du tas d'entrée. Cela peut impliquer des structures mathématiques assez complexes (par exemple, dans ce cas, travailler dans une catégorie qui garantit que tout fonctionne modulo un groupe de permutation approprié).