Planche : 1


Termes et relations pour la sémantique


Planche : 2


Objectif

La sémantique ?


Planche : 3


Moyens


Planche : 4


Comment définir un langage de programmation ?


Planche : 5


Sémantique ?

Savoir que « My taylor is rich » est bien de l’anglais ne suffit pas.

Savoir que « fun x -> x * x » est bien du Caml ne suffit pas. On veut en plus savoir ce qui se passe quand on applique ce programme (cette fonction) à une entrée.

(fun x -> x * x), 416

Définir la sémantique c’est définir une relation P, ev, où P est un programme (ici syntaxe abstraite), e est une entrée (ici une valeur) et v le résultat (ici une valeur).

Si à P et e donnés correspond un unique v, on définit une fonction.

Mais comment définir ↪ ? langue naturelle ou mathématiques ?


Planche : 6


Connaître la sémantique pour programmer

Une évidence : pour bien programmer il faut savoir ce que font les programmes.

Or ce n’est pas toujours évident,

Pourtant on essaie quand même de spécifier le comportement des programmes en langue dit naturelle (français, anglais…).


Planche : 7


Un exemple de programme

class Test {

   static int n = 0 ;

   static int f(int xint y) { return x ; }

   static int g(int z) { n = n + z ; return n ; }

   public static void main(String [] arg) {
     System.out.println(f(g(2), g(7))) ;
   }
}

Quel est l’affichage ? « 2 », car en Java les arguments sont évalués de la gauche vers la droite, comme précisé dans la définition du langage.


Planche : 8


Autre exemple

let n = ref 0

let f x y = x

let
 g z = n := !n + z ; !n

let
 () = Printf.printf "%i\n" (f (g 2) (g 7))

Si on essaie (ranger dans test.ml et exécuter ocaml test.ml), on obtient 9.

Ici attention, la documentation de OCaml précise que l’ordre d’évaluation des arguments n’est pas spécifié.


Planche : 9


Autre exemple pertinent

let f x = let g x = x in g

let
 x = 3

let () = Printf.printf "%i\n" (f 1 2)

Le programme affiche : « 2 ».

On comprend un peu mieux en réécrivant :

let f y z = z

let
 x = 3

let () = Printf.printf "%i\n" (f 1 2)

Planche : 10


Outils dont les entrées sont des programmes

Ce sont :

les interpréteurs, les compilateurs, les analyseurs de programmes…

La sémantique est indispensable pour…


Planche : 11


Exemple de transformation de programme

Soit la fonction f :

let f x y = x

Peut-on « optimiser » tout appel f e1 e2 en e1 ?

Non, car par exemple

let z = f 1 (Printf.printf "%i\n" 2 ; 2)

Mais on peut transformer :

f e1 e2let x = e1 and y = e2 in x

Planche : 12


La langue naturelle


Planche : 13


Un exemple de sémantique en langue naturelle

The while statement executes an Expression and a Statement repeatedly until the value of the Expression is false.

    WhileStatement:
            while ( Expression ) Statement

A while statement is executed by first evaluating the Expression. If the result is of type Boolean, it is subject to unboxing conversion (§5.1.8). If evaluation of the Expression or the subsequent unboxing conversion (if any) completes abruptly for some reason, the while statement completes abruptly for the same reason. Otherwise, execution continues by making a choice based on the resulting value:

If the (possibly unboxed) value of the Expression is false the first time it is evaluated, then the Statement is not executed.

La suite dans la documentation de Java.


Planche : 14


Sémantique formelle

Dans ce cours…


Planche : 15


Relations

Planche : 16


Définition d’un ensemble (d’une relation)


Planche : 17


Premier théorème du point fixe





Preuve : Considérer limfi(⊥), c’est le plus petit point fixe de f.


Planche : 18


Preuve

Soit ui = fi(⊥).


Planche : 19


Graphiquement

[0,1], ≤ est faiblement complète.

+ faiblement complète ?


Planche : 20


Second théoreme du point fixe



Preuve : la borne inférieure de C = { cf(c) ≤ c } est le plus petit point fixe de f.


Planche : 21


Preuve

Soit p borne inférieure des « contractés » C = { cEf(c) ≤ c }.


Planche : 22


Graphiquement

[0,1], ≤ est fortement complète.

+ fortement complète ?


Planche : 23


Quelques exemples


Planche : 24


Exemple encore (qui va servir)


Planche : 25


Définition inductive par l’exemple

P ensemble des entiers pairs peut être défini par les règles suivantes.

0 ∈ P 
(n ∈ P ⇒ n+2 ∈ P)

Que l’on note plutôt.

 
0 ∈ P
n ∈ P
n+2 ∈ P
ou même
 
0
n
n+2

Autrement dit, P contient 0 et P est clos par la fonction nn+2.

Il est remarquable que P n’est pas le seul ensemble satisfaisant ces deux propriétés, par ex. ℕ convient.

Alors ? P est le plus petit de ces ensembles.


Planche : 26


Vérifions

Soient les fonctions f1 : ℕ0 → ℕ, qui à () associe 0 ; et f2 : ℕ → ℕ, qui à x associe x+2.

Soit ensuite F : 2 → 2 qui à X associe :

y ∣ y = f1() } ⋃ { y ∣ ∃ x ∈ Xf2(x) = y }

Soit si on veut :

F(X) = { 0 } ⋃ { x+2 ∣ x ∈ X }

F est…


Planche : 27


Et selon les théorèmes

Les deux règles définissent donc un ensemble d’entiers P, qui est le plus petit point fixe de F.

Et au passage P est l’ensemble des entiers pairs (2ℕ), en effet.

Mais ce qui compte c’est que nous avons pu définir un ensemble (d’entiers) inductivement.


Planche : 28


Cas général (poly)

Pour tout ensemble E. On peut définir un sous-ensemble A de E par des fonctions fk (eventuellement partielles) de Enk dans E, souvent notées comme les règles :

x1 ⋯ xnk
fk(x1, …, xnk)

Ce sous-ensemble est le plus petit point fixe de la fonction F de 2E dans 2E.

F(X) =
 
k ∈ K
 { fk(x1, …, xnk) ∣ x1, …, xnk ∈ Xnk}

C’est aussi le plus petit ensemble clos par les règles.

Cela permet en particulier de définir des relations, qui sont des ensembles (de paires par ex.)


Planche : 29


Exemple de relation définie inductivement

Soit Σ alphabet (ensemble de caractères notés a, b)

Soit Σ ensemble des mots (suites finies sur Σ, notés m, n).

Relation de facteur mn

 
є ≼ m
 
m ≼ m
m1 ≼ n1           m2 ≼ n2
m1m2 ≼ n1n2
m ≼ n
m ≼ n n
m ≼ n
m ≼ n′ n

Planche : 30


Dérivation

Une dérivation ou arbre de preuve est un arbre dont les nœuds sont l’application des règles (les fk).

L’existence d’une dérivation aboutissant en e prouve que e est dans Fi(∅) (et donc dans le plus petit point fixe).

Par ex, acabcde.

a ≼ a          
c ≼ c
c ≼ cde
c ≼ bcde
ac ≼ abcde

Réciproquement, tout élément du plus petit point fixe est l’aboutissement d’une dérivation (récurrence sur i).


Planche : 31


Induction structurelle

Pour prouver une propriété P des éléments du plus petit point fixe.

Il peut suffire de montrer que P est héreditaire, c’est à dire P(x1), …P(xnk) entraîne P(fk(x1,…,xnk)).

Preuve L’ensemble { eEP(e) } est clos par F et second théorème.

Par ex. pour montrer que si m est un facteur de n, alors m est plus court que n :

ℓ(є) ≤ ℓ(m)
ℓ(m) ≤ ℓ(m)
ℓ(m1) ≤ ℓ(n1) ∧ ℓ(m2) ≤ ℓ(n2) ⇒ ℓ(m1m2) ≤ ℓ(n1n2)
ℓ(m) ≤ ℓ(n) ⇒ ℓ(m) ≤ ℓ(nn′)

Conséquence directe des propriétés de la longueur ℓ(є) = 0 et ℓ(m1m2) = ℓ(m1) + ℓ(m2).


Planche : 32


Autre exemple d’induction structurelle

Nous avons enfoncé une porte ouverte en prouvant que les règles

0
 
n
n+2

définissent bien l’ensemble des entiers pairs.

En effet, si nP alors n est pair (induction structurelle). Réciproquement, pour tout n = 2k, il existe une dérivation.



On pourrait de même enfoncer une porte (un peu moins) ouverte en démontrant que mn, ssi m est une sous-suite de n au sens usuel.


Planche : 33


Un dernier exemple

Soit une relation R, binaire, sur un ensemble E (donc un sous-ensemble du produit cartésien E × E).

On définit R*, la fermeture reflexive et transitive de R inductivement :

 
e R* e
e R e
e R* e
e1 R* e2           e2 R* e3
e1 R* e3

Il est assez immédiat que

e R* e

∃  n ∈ ℕ, ∃  (e1, …, en−1) ∈ En−1e = e0 R e1 R ⋯ R en−1 R en = e

(Induction structurelle.)


Planche : 34


Termes

Planche : 35


Insistons

Nous avons déjà dit que l’expression (Caml par ex.) « 1-2-3 » est pour nous le terme :

De même, nous lisons « f x y », nous comprenons (application explicitée) :

Mais sommes nous bien sûrs de savoir ce qu’est un terme ?


Planche : 36


Termes sans variables


Planche : 37


Exemple : expressions arithmétiques

Et les expressions arithmétiques sont définies.

Une définition à peu près équivalente en Caml.

type t =
Add of t * t | Sub of t * t | Mul of t * t | Div of t * t 
Int of int (* à peu près une infinité *)

let e = Sub (Sub (Int 1, Int 2), Int 3)



Quand on veut écrire une expression arithmétique sans dessiner un arbre, il est préférable de donner (presque) toutes les parenthèses.

t = -(-(1,2),3)    ou encore    t = (1-2)-3

Planche : 38


Sémantique

Définir une sémantique des expressions arithmétiques, c’est définir une relation en entre les expressions et les entiers.

Nous donnons trois techniques.


Planche : 39


Sémantique naturelle

Au symbole n on associe l’entier n, au symbole + on associe l’addition (fonction ℕ2 dans ℕ, notée +)

 
n ↪ n
e1 ↪ n1           e2 ↪ n2
e1 +e2 ↪ n1+n2

Une trivialité, mais

let rec eval e = match e with
Int n -> n (* NB: n et n différents *)
Add (e1,e2) -> (* NB:  + et + différents *)
   let n1 = eval e1 and n2 = eval e2 in
   n1+n2
  …

Certes la présentation par règles d’inférences est plus neutre.


Planche : 40


Sémantique à petit pas

On définit donc la relation → sur les expressions, par une infinité d’axiomes :

0+0 → 0
0+1 → 1
1+1 → 2

Puis par huit règles d’inférence (qui ici définissent une congruence : ou peut réduire tout sous-terme).

e1 → e1
e1 +e2 → e′ 1 +e2
e2 → e2
e1 +e2 → e 1 +e2

Et on pose ↪ = →*.

Note : Les valeurs sont maintenant nécessairement un sous-ensemble des expressions.


Planche : 41


Sémantique dénotationelle

C’est une fonction des expressions dans les entiers, définie explicitement.

[[n]] = n
[[e1 + e2]] = [[e1]] + [[e2]]

Les différences avec la sémantique naturelle n’apparaissent que sur des exemples plus compliqués.

Fun x -> x+1

Est la « vraie » fonction sur ℕ, qui à x associe le successeur de x.

let rec fact n = if n <= 0 then 1 else n * fact (n-1)

Est la factorielle : 0 ↦ 1, 1 ↦ 1 × 1, 2 ↦ 2 × 1 × 1, …nn × (n−1) × ⋯ × 1 × 1, …


Planche : 42


Les variables

Comment donner une valeur à par exemple « x+x » ?

Simple ! Il suffit de remplacer x par un terme fixé. C’est la substitution [e\x].

Un premier exemple simple : expressions arithmetiques + variables.

[e\x]x = e
[e\x]y = y (y ≠ x)
[e\x](e1 + e2) = ([e\x]e1) + ([e\x]e2)

Et donc [1+1\x](x+x) = (1+1)+(1+1).

Noter : « = » et non «  », c;est une meta-opération (i.e. [ex\x]e ne fait pas partie de la syntaxe des termes).


Planche : 43


Un langage pour les fonctions

Nous avons déjà écrit par exemple nn + 2.

En Caml on écrit : « fun n -> n+2 ».

Il est remarquable que mm + 2 et fun m -> m+2 désignent la même fonction : la variable est muette.

L’expression fun x -> e, où x est une variable (ci-dessus n ou m) et e est une expression (ci-dessus n+2 ou m+2), est à comprendre comme l’arbre :

Vocabulaire : On dit que la construction fun lie la variable x dans la sous-expression e. L’occurence de x dans e est ici libre.


Planche : 44


Autres constructions liantes

Un autre exemple (Caml) est « let x = ex in e », à lire comme :

x est liée dans le sous-terme e.

Considérer aussi : « let rec x = ex in e », où x est liée dans ex et e.


Planche : 45


Expressions arithmétiques + variables

Une expression e est :

Dans let  x = ex in e, x est liée dans e.

type var = …

type t =
Add of t * t | Sub of t * t | Mul of t * t | Div of t * t
Int of int
Var of var
Let of var * t * t

Planche : 46


Variables d’une expression

Définies par induction sur la structure des expressions.

V(0) = ∅
V(x) = { x }
V(e1 +e2) = V(e1) ⋃ V(e2)
V(let x = ex in e) = { x } ⋃ V(ex) ⋃ V(e)
module VarSet =
  Set.Make
    (struct type t = var let compare = … end)

let rec vars e = match e with
Int _ -> VarSet.empty
Add (e1,e2) | Sub (e1,e2) | Mul (e1,e2) | Div (e1,e2) ->
    VarSet.union (vars e1) (vars e2)
Var x -> VarSet.singleton x
Let (x,ex,e) ->
    VarSet.add (VarSet.union (vars ex) (vars e)) x

Planche : 47


Variables libres

Ce sont les variables non-liées, c’est-à-dire celles dont la valeur doit être fournie.

F(x+1)=x  }
F(x+1+y)=x, y  }
F(let x = 1 in x+1)=
F(let x = 1 in x+1+y)=y  }
F(0) = ∅
F(x) = { x }
F(e1 +e2) = F(e1) ⋃ F(e2)
F(let x = ex in e) = V(ex) ⋃ 
V(e) ∖ { x }

Et aussi,

F(fun x -> e) = F(e) ∖ {  x  }

Planche : 48


Calcul des variables libres

let rec free e = match e with
Int _ -> VarSet.empty
Add (e1,e2) | Sub (e1,e2) | Mul (e1,e2) | Div (e1,e2) ->
    VarSet.union (free e1) (free e2)
Var x -> VarSet.singleton x
Let (x,ex,e) ->
    VarSet.union (free ex) (VarSet.remove (free ex)

Planche : 49


Sémantique naturelle des expressions avec variables

Une expression peut maintenant contenir des variables (libres).

Une solution simple : l’environnement. La relation sémantique est maintenant de la forme

Γ ⊢ e ↪ v

Où ici,


Planche : 50


Sémantique des expressions avec variables

   
Γ ⊢ n ↪ n
 Γ(x) = n 
Γ ⊢ x ↪ n
Γ ⊢ e1 ↪ n1          Γ ⊢ e2 ↪ n2
Γ ⊢ e1 + e2 ↪ n1 + n2

Planche : 51


Programmation

Un environnement est une liste de paires x × v.

type env = (var * intlist


let
 rec assoc x env = match env with
| [] -> raise Not_found
| (y,vy)::env ->
   if x=y then vy
   else assoc x env
(* Disponible comme List.assoc *)


let rec eval env e = match e with
Var x -> assoc x env
Let (x,ex,e) ->
    let vx = eval env ex in
    eval ((x,vx)::enve
| … (* Comme avant ou presque *)

Planche : 52


Vers la sémantique à petit pas

Comment définir → (une étape de calcul) sur un terme qui contient let ? L’idée est que on peut effectuer la liason let x = ex in e en remplaçant x par ex dans e.

Une substitution est tout simplement une fonction (de domaine fini) des variables dans les… termes. On note [ex\x] la substitution qui à x associe ex.

Les substitutions sont des morphismes, c-à-d respectent la structure de terme.

Les substitutions s’appliquent aux variables libres seulement :

[2\x] (x+1)=2+1
[2\x] (fun x -> x+1)=fun x -> x+1
[2\x] (let x = x+x in x)=let x = 2+2 in x

Planche : 53


Définition de la substitution, premier essai

Remplacer les variables libres par leurs définitions :

[e\x]0 = 0
[e\x]x = e
[e\x]y = y
[e\x]e1 + e2 = [e\x]e1 +  [e\x]e2
[e\x](let x = e1 in e2) = let x = [e\x]e1 in e2
[e\x](let y = e1 in e2) = let y = [e\x]e1 in [e\x]e2

Et de même :

[e\x](fun x -> e1) = fun x -> e1
[e\x](fun y -> e1) = fun y -> [e\x]e1

Planche : 54


Mais attention

Il semble donc que l’on puisse réduire let x = ex in e en [ex\x] (e).

Soit la fonction qui ajoute y libre à son argumemt.

fun x -> x + y

Sous-expression par exemple de :

let y = ey in fun x -> x + y

En substituant…


Planche : 55


Alpha-équivalence

Dans l’exemple [x\y](fun x -> x+y), nous sommes victimes de la capture de la variable libre x, qui devient liée en traversant la liaison fun x -> 

Or

L’alpha-équivalence est l’égalité modulo le changement des variables muettes.


Planche : 56


Une définition possible de la substitution

En évitant les captures.

[e\x](let x = e1 in e2) = let x = [e\x]e1 in e2
[e\x](let y = e1 in e2) = let z = [ex\x]e1 in [ex\x]([z\y]e2)

z ci-dessus n’appartient ni à F(ex), ni à F(e2).

C’est bien compliqué.


Planche : 57


Programmer la substitution

En pratique : z est une variable « fraiche », c’est à dire une variable nouvelle.

type var = int

let
 fresh_var =
  let count = ref 0 in
  fun () ->
    let v = !count in
    count := !count+1 ;
    v

let
 rec subst x ex e = match e with
Int _ -> e
Add (e1,e2) -> Add (subst x ex e1subst x ex e2)
  … (* Sub, etc. idem *)
Var y ->  if x = y then ex else e
  …

Planche : 58


Programmer la substitution, cas délicat

Let (yeye) ->
    if x=y then 
      Let (ysubst x ex eye)
    else 
      let z = fresh_var () in
      Let (zsubst x ex eysubst x ex  (subst y (Var ze))

Pas le plus efficace, certainement… Mais suffisant pour définir le calcul sur le let.

Pour définir une étape de calcul, il reste à


Planche : 59


En TP


Ce document a été traduit de LATEX par HEVEA