Planche : 1


Interprétation (environnements)


Planche : 2


A
Un petit tour autour du point fixe.
B
De l’évaluaton à l’interprétation : environnements.
C
Interprétation par valeur.

Planche : 3


Point fixe et réduction

On s’intéresse aux « solutions » d’équations du genre :

x =s t, avec x variable, et t terme de PCF où x apparaît.

« =s » est l’égalité dans la sémantique.



Soit u = Fix x -> t avec

u—→[u\x]t

C’est-à-dire, inventons le point fixe explicite.



On pose l’exigence sémantique t—→t′ ⇒ t =s t′.

Il est alors évident que u est solution de x =s t.


Planche : 4


Point fixe construit

Une première étape : « abstraire x dans t ».

x = (Fun y -> t′) x, avec t′ = [y\x]t.

Soit un terme u tel que u—→*Φ u—→[u\y]t′,

Soit en fait (car t′ = [y\x]t) :

u—→*[u\y][y\x]t = [u\x]t

C’est à dire que u est solution.


Planche : 5


Construction explicite

De u tel que u ←→* Φ u.

Soit la fonction suivante curry :

Fun f -> (Fun x -> f (x x)) (Fun x -> f (x x))

On va montrer que pour tout terme Φ :

curry Φ ←→* Φ (curry Φ)

(←→* fermeture réflexive-symétrique-transitive de —→, entraîne =s).


Planche : 6


Démonstration de curry Φ ←→* Φ (curry Φ)

Posons v = Fun x -> f (x x) Par une β-réduction il vient :

v v—→f (v v)

Lemme : t—→t′ ⇒ [u\x]—→[u\x]t′

Donc : [Φ\f](v v)—→[Φ\f]f (v v). (1)

Or (une β) : curry Φ—→[Φ\f](v v) (2)

Et c’est fini :

curry Φ —→ [Φ\f](v v) —→ [Φ\f](f (v v)) = Φ ([Φ\f](v v)) ←— Φ (curry Φ)

Planche : 7


Astuce ultime

Notons F = Fix x -> t. On effectue les substitution de x dans t, dès que prêt :

F → [F\x]t → [[F\x]t\x]t → [[[F\x]t\x]t\x]t → …

On obtient un terme « infini », représentable par un graphe :


Planche : 8


Plus clair sur un exemple


Planche : 9


Fabriquer un tel graphe


Planche : 10


Environnements


Planche : 11


Appel par nom

t1↪nFun x -> t3           [t2\x]t3↪nv
t1 t2↪nv
Fun x -> t↪nFun x -> t
[t1\x]t2↪nv
Let x = t1 In t2↪nv
[(Fix x -> t)\x]t↪nv
Fix x -> t↪nv
t1↪n0           t2↪nv
Ifz t1 Then t2 Else t3↪nv
t1↪nn (n ≠ 0)           t3↪nv
Ifz t1 Then t2 Else t3↪nv
t1↪nn1         t2↪nn2
t1 op t2↪nn1 op n2
n↪nn

Planche : 12


Appel par valeur

t1↪vFun x -> t3           t2↪vv2           [v2\x]t3↪vv
t1 t2↪vv
Fun x -> t↪vFun x -> t
t1↪vv1           [v1\x]t2↪vv
Let x = t1 In t2↪vv
[(Fix x -> t)\x]t↪vv
Fix x -> t↪vv
t1↪v0           t2↪vv
Ifz t1 Then t2 Else t3↪vv
t1↪vn (n ≠ 0)           t3↪vv
Ifz t1 Then t2 Else t3↪vv
t1↪vn1         t2↪vn2
t1 op t2↪vn1 op n2
n↪vn

Planche : 13


Substitution (dans un évaluateur)

Pour évaluer (Fun x -> t1) t2,on calcule [t2\x]t1 (ou [v2\x]t1)

Si t2 n’a pas de variable libre, alors la substitution est simplifiée :

[t2\x](Fun x -> t1) = Fun x -> t1
[t2\x](Fun y -> t1) = Fun y -> [t2\x]t1

Et c’est tout, car y n’est certainement pas libre dans t2 .

Toutefois,

On veut mélanger évaluation et substitution, comme on avait mélangé recherche de radical et réduction.


Planche : 14


Substitution

Pour évaluer (Fun x -> (x * x) + x) 4 — noté (Fun x -> t) 4.

(Fun x -> t)↪n(Fun x -> t)
         
4↪n4         4↪n4
4 * 4↪n16
          4↪n4
(4 * 4) + 4↪n20
(Fun x -> t) 4↪n20

Planche : 15


Une alternative


Planche : 16


Priorité (à droite)

Valeur de Let x = 4 In Let x = 5 In x ?

En toute rigueur

[5\x]x↪n5
Let x = 5 In x↪n5
[4\x](Let x = 5 In x)↪n5
Let x = 4 In Let x = 5 In x↪n5

Planche : 17


Priorité avec les environnements

Un environnnement contient maintenant plusieures définitions x=n, et on applique la priorité.

[x = 4, x = 5] ⊢ x↪5
[x = 4] ⊢ Let x = 5 In x↪5
 ⊢ Let x = 4 In Let x = 5 In x↪5

Planche : 18


Formalisation de la liaison lexicale

On pourrait écrire :

[…, x=n] ⊢ x↪n
[…] ⊢ x↪n
[…, y=ny] ⊢ x↪n
[…, x=n] ⊢ t↪v
[…] ⊢ Let x = n In t↪v

On préfère une notation plus abstraite.

E(x) = n
E ⊢ x↪n
E ⊕ [x = n] ⊢ t↪v
E ⊢ Let x = n In t↪v

Les environnements sont des fonctions des variables. Avec un opérateur ⊕ défini par

(E ⊕ [x = n])(x) = n
(E ⊕ [x = n])(y) = E(y)

Planche : 19


Implémentation de la liason lexicale

Avec de bêtes listes d’associations :

type 'a env = (Var.t * 'a) list

Opération « ⊕ ».

(* Ajouter l'association de x à t *)
let add x t env = (x,t)::env

Recherche :

let rec find x env = match env with
| [] -> raise (Error ("Variable libre : " ^ x))
| (y,t)::env ->  if x=y then t else find x env

(C’est List.assoc).

La priorité à droite devient la priorité à gauche :

find x ((x,t1)::(x,t2)::…) → t1

Mais c’est pareil.


Planche : 20


Exemples

Généralisons les définitions des environnements : [x = t] (et non plus slt [x=n]).

Recommençons.


Planche : 21


Le glaçon (thunk)

Dans l’exemple précédent, nous avons négligé la définition de la substitution.

Donc on considère

[5\x](Let y = 4 + x In y + 3)

C’est-à-dire

Let y = [5\x](4 + x) In [5\x](y + 3)

Principe de l’interprétation = ne pas substituer par avance.

Donc, les environnements contiennent des applications de substitutions retardées : des glaçons, notés <t•E>.


Planche : 22


Reprenons

Le terme clos 5 se représente facilement comme le glaçon <5•[]>


Planche : 23


Les règles de l’interprétation

Sachant que l’environnement lie des variables à des glaçons, qui sont eux-mêmes des paires « terme × environnement ».

Deux règles « faciles »

E(x) = <t•E′>         E′ ⊢ t↪v
E ⊢ x↪v
E ⊕ [x = <t1•E>] ⊢ t2↪v
E ⊢ Let x = t1 In t2↪v

Mais aussi, bien évidemment, l’arithmétique un peu aménagée.

E ⊢ n↪n
E ⊢ t1↪n1         E ⊢ t2↪n2
E ⊢ t1 op t2↪n1 opn2

La conditionnelle pareillement aménagée.

E ⊢ t1↪0           E ⊢ t2↪v
E ⊢ Ifz t1 Then t2 Else t3↪v
E ⊢ t1↪n (n ≠ 0)           E ⊢ t3↪v
E ⊢ Ifz t1 Then t2 Else t3↪v

Et le point fixe pareillement aménagé.

E ⊕ [x = <Fix x -> t•E>] ⊢ t↪v
E ⊢ Fix x -> t↪v

Planche : 24


Fermeture

Nous avions la règle d’évaluation des fonctions

Fun x -> t↪Fun x -> t

Quelle peut-être la règle d’interprétation ?

E ⊢ Fun x -> t↪<x•t•E>

On a très envie de dire <Fun x -> t•E>, mais cela compliquerait exagérément le concept de valeur. On définit plutôt la fermeture <x•t•E> comme ce glaçon là.

Nouvelle règle de l’application.

E ⊢ t1↪<x•t′1•E′>           E′ ⊕ [x = <t2•E>] ⊢ t′1↪v
E ⊢ t1 t2↪v

NB : Les valeurs ne sont plus des termes.


Planche : 25


Impact de la fermeture

Quelle est la valeur du terme

Let x = 4 In
Let
 f = Fun y -> y + x In
Let
 x = 5 In
f
 x

Le point crucial est de savoir à quoi se rapporte x dans y + x.

À la définition de x active au moment de la définition de f (et non pas de son application).

Et donc le résultat est 9 (et non pas 10).

La fermeture est ici cruciale, f est liée au glaçon

<Fun y -> y + x•[x=<4•[ ]>]>

Dont la valeur est la fermeture : <y•y + x•[x=<4•[ ]>]>.

Et y + x s’évalue dans [x=<4•[ ]>, y = <x•[…,x = <5•⋯>]>].

[x = …, f = …, x = <5•[…]>] ⊢ f 5↪10
[x=…, f= <Fun y -> y + x•[x=<4•[ ]>]>] ⊢ Let x = 5 In ⋯↪10
[x = <4•[ ]>] ⊢ Let f = Fun y -> y + x In ⋯↪10
 ⊢ Let x = 4 In ⋯↪10

Planche : 26


Toutes les règles

Les valeurs v sont des entiers n ou des fermetures <x•t•E>.

Les environnements E lient les variables x à des glaçons <t•E>.

E ⊢ Fun x -> t↪n<x•t•E>
E ⊢ t1↪n<x•t′1•E′>           E′ ⊕ [x = <t2•E>] ⊢ t′1↪nv
E ⊢ t1 t2↪nv
E ⊕ [x = <Fix x -> t•E>] ⊢ t↪nv
E ⊢ Fix x -> t↪nv
E(x) = <t•E′>         E′ ⊢ t↪nv
E ⊢ x↪nv
E ⊕ [x = <t1•E>] ⊢ t2↪nv
E ⊢ Let x = t1 In t2↪nv
E ⊢ n↪nn
E ⊢ t1↪nn1         E ⊢ t2↪nn2
E ⊢ t1 op t2↪nn1 opn2
E ⊢ t1↪n0           E ⊢ t2↪nv
E ⊢ Ifz t1 Then t2 Else t3↪nv
E ⊢ t1↪nn (n ≠ 0)           E ⊢ t3↪nv
E ⊢ Ifz t1 Then t2 Else t3↪nv

Planche : 27


Interpréter par valeur


Planche : 28


Notable simplification

Les règles de l’évaluation par valeur.

t1↪vFun x -> t3           t2↪vv2           [v2\x]t3↪vv
t1 t2↪vv
t1↪vv1           [v1\x]t2↪vv
Let x = t1 In t2↪vv

Les substitutions retardées sont de la forme [v\x]⋯

Comme v est n (entier) ou <x•t•E> (fermeture), plus besoin de glaçon.

Malheureusement, il y a la récursion.

[(Fix x -> t)\x]t↪vv
Fix x -> t↪vv

Planche : 29


Interprétation par valeur, simplicité ?

Récursion avec environnement (et glaçon) :

E ⊕ [x = <Fix x -> t•E>] ⊢ t↪vv
E ⊢ Fix x -> t↪vv

Donc : les environnements lient des variables à des valeurs v, ou à des glaçons de la forme <Fix x -> t•E> (exclusivement).

Deux règles pour les variables au lieu d’une.

E(x) = v
E ⊢ x↪vv
E(x) = <Fix x -> t•E′>           E′ ⊢ Fix x -> t↪vv
E ⊢ x↪vv

Et changer les règles de l’application et du Let.

E ⊢ t1↪v<x•t′•E′>           E ⊢ t2↪vv2           E′ ⊕ [x = v2] ⊢ t′↪vv
E ⊢ t1 t2↪vv
E ⊢ t1↪vv1           E ⊕ [x = v1] ⊢ t2↪vv
E ⊢ Let x = t1 In t2↪vv

Et c’est tout, mais il est dommage d’avoir des glaçons, juste pour coder la récursion.


Planche : 30


Toutes les règles

Les valeurs v sont des entiers n ou des fermetures <x•t•E>. Les environnements E lient les variables x à des valeurs v ou à des glaçons de la forme <Fix x -> t•E>.

E(x) = v
E ⊢ x↪vv
E(x) = <Fix x -> t•E′>           E′ ⊢ Fix x -> t↪vv
E ⊢ x↪vv
E ⊢ t1↪v<x•t′•E′>           E ⊢ t2↪vv2           E′ ⊕ [x = v2] ⊢ t′↪vv
E ⊢ t1 t2↪vv
E ⊢ t1↪vv1           E ⊕ [x = v1] ⊢ t2↪vv
E ⊢ Let x = t1 In t2↪vv
E ⊕ [x = <Fix x -> t•E>] ⊢ t↪vv
E ⊢ Fix x -> t↪vv
E ⊢ n↪vn
E ⊢ t1↪vn1         E ⊢ t2↪vn2
E ⊢ t1 op t2↪vn1 opn2
E ⊢ t1↪v0           E ⊢ t2↪vv
E ⊢ Ifz t1 Then t2 Else t3↪vv
E ⊢ t1↪vn (n ≠ 0)           E ⊢ t3↪vv
E ⊢ Ifz t1 Then t2 Else t3↪vv

Planche : 31


Une remarque

En appel par valeur, Fix sert surtout à définir des fonctions.

Let fact =
  Fix f Fun x -> Ifz x Then 1 Else x * f (x - 1) In
…

Interprétation de Fix f -> Fun x -> t, noté FixFun f x -> t

E ⊕ [f = <FixFun f x -> t•E>] ⊢ Fun x -> t↪<x•t•E′>
E ⊢ Fix f -> Fun x -> t↪<x•t•E′>

Avec

E′ = E ⊕ [f = <FixFun f x -> t•E>]

Planche : 32


Première variante

Si Fix f -> ⋯ est nécessairement de la forme FixFun f x -> t, on peut remplacer les glaçons par une forme particulière de fermeture.

La nouvelle fermeture <f•x•t•E>, qui représente la fermeture à trois composants de la forme

<x•t•[f = <FixFun f x -> t•E>]>

On pose alors directement.

E ⊢ FixFun f x -> t↪v<f•x•t•E>

On a alors une règle supplémentaire pour appliquer les nouvelles fermetures…

E ⊢ t1↪v<f•x•t•E′>           E ⊢ t2↪vv2           E′ ⊕ [f = <f•x•t•E′>, x=v2] ⊢ t↪vv
E ⊢ t1 t2↪vv

Planche : 33


Astuce

Si on représente une fermeture à trois composantes <x•t•E> comme une fermeture à quatre composantes <_f•x•t•E>, alors la nouvelle règle d’application des fermetures remplace l’ancienne !

On pose donc :

_f ∉F(t)
E ⊢ Fun x -> t↪v<_f•x•t•E>

Et on supprime l’ancienne règle d’application.

E ⊢ t1↪v<x•t′•E′>           E ⊢ t2↪vv2           E′ ⊕ [x = v2] ⊢ t′↪vv
E ⊢ t1 t2↪vv

Mais c’est bien artificiel (liaison de _f inutile).


Planche : 34


Encore plus malin

Revenons à l’interprétation de t = FixFun f x -> b (page 33)

 
E ⊕ [f = g] ⊢ Fun x -> b↪v<x•b•E ⊕ [f = g]>
E ⊢ FixFun f x -> b↪v<x•b•E ⊕ [f = g]>

Où g note le glaçon <FixFun f x -> b•E>.

Anticipons maintenant l’interprétation de g. Une fois

<x•b•E ⊕ [f = g]>

Et encore une fois

<x•b•E ⊕ [f = <x•b•E ⊕ [f = g]>]>

Etc. on obtient une fermeture ordinaire, mais bouclée, avec :

C = <x•b•E ⊕ [f = C]>

Planche : 35


Un graphe


Planche : 36


Encore plus fort

Soit un interpréteur (qui est au final un programme)

Reste à construire le graphe, pour interpréter FixFun f x -> b dans l’env. E, par ex. en Caml.

type value = (* Les valeurs *)
| Integer of int | Closure of Var.t * Ast.t * env
and
 env = (Var.t * value) list (* les environnements *)

let rec inter E t = match t with
…
| Fix (f,Fun (x,b)) ->
    let rec clo = Closure (x, b, (f,clo)::E) in
    clo

Avantage définitif : une seule sorte de fermeture.


Planche : 37



Ce document a été traduit de LATEX par HEVEA