Plan des 4 prochains cours


Partie: I
Les principes du typage




Plan





Introduction I



Le typage
Diviser l'espace des valeurs en collections: un type est une collection de valeurs partageant une même propriété

Les types sont toujours plus ou moins vérifiés:
Intérêts du typage statique



Introduction II



Remarque
On étudie ici les principes du typage statique


Qu'est qu'un type?

Typer une expression



Introduction III



Vérification des types
le programmeur associe un type à chaque déclaration d'identificateur et le compilateur vérifie la correction (e.g, C, C++, Pascal, Ada,...)

int addition (int x1, int x2)
{  int temp;
   temp = x1 + x2;
   return(temp); }

Synthèse des types
les types sont devinés (on dira synthétisés ou inférés) par le compilateur par une analyse des contraintes d'utilisation des variables (e.g, Caml, SML,...)

#let addition x1 x2 =
   let temp = x1 + x2 in
   temp;;
addition : int -> int -> int = <fun>



Introduction IV


``le programme P est de type ty''

P : ty


Vérification

Synthèse

Intérêts de la synthèse




Quelques exemples I



Typer une expression:
parcourir l'expression en associant un type unique à chaque sous-expression


Exemple:
2 + (4 * 5)  

2:int    
4:int     5:int

4*5:int

2+(4*5):int

Un axiome:

i : int

Une règle de déduction:

e1 : int     e2 : int

e1 + e2 : int




Quelques exemples II


·
·
·
P1:ty1
   
·
·
·
Pn:tyn

C(P1,...,Pn):ty




Quelques exemples III


Un programme est bien typé lorsque l'on peut construire une telle déduction


Typer:
2 + (true * 5)

2:int    
true:bool     5:int

true*5:?

2+(true * 5):?


Question:
Que faire en présence d'identificateurs?

#let x = 1;;
x : int = 1
#let y = 2 + (4 * x);;
y : int = 6
On introduit l'environnement de typage.


Notations, définitions I


``Dans l'environnement , le programme P a le type ty''
|- P:ty

L'environnement de typage

Notre langage des types



Le typage de CAML I



Constantes

 |-  true : bool
 
 |-  false : bool
 
 |-  42 : int


Identificateurs
(Ident)
acces (x,) = ty

 |-  x : ty


Conditionnelle
if cond then e1 else e2

(Cond)
 |-  cond : bool    |-  e1 : ty    |-  e2 : ty

 |-  if   cond   then  e1    else  e2  : ty




Le typage de CAML II



Application de fonction

Soient e1 une expression (fonctionnelle) et e2 une expression. L'application e1  e2 est typée avec la règle :

(App)
 |-  e1 : t1 ® t2      |-  e2 : t1

 |-  e1 e2 : t2


Exemples:

fact (fact 5)

2 + (4 * 5)


Remarque:



Le typage de CAML III



Fonctions: function x -> corps
Typage dans

  1. Choisir une nouvelle variable de type, t, pour désigner le type de x. t est donc une inconnue.

  2. Typer corps dans ë (x,t) <|  û. Fait apparaître des contraintes de typage (t = t1).
  3. Faire le bilan des contraintes :
    1. contraintes contradictoires : erreur de typage.
    2. variables de type toutes déterminées:
      si x : t1 et si corps: t2, alors
      function x -> corps: t1 ® t2
    3. certaines variables de type restent indéterminées: polymorphisme (plus tard)
(Fun)
ë (x:t1) <|  û  |-  corps : t2

 |-  function x -> corps : t1 ® t2




Le typage de CAML IV



Déclarations globales

let idf = exp;;

#let x = 1;;
x : int = 1
#let y = x+x;;
y : int = 2
#let y = y+2;;
y : int = 4
  1. Typage dans l'env. global courant
  2. typage de exp
     |-  exp : ty
  3. le nouvel environnement global ' = ë (idf:ty) <|  û



Le typage de CAML V



Déclarations locales
let idf = exp1 in exp2;;
#let z = 1 in z+2;;
- : int = 3
#let y = z+1;;
Entrée interactive:
>let y = z+1;;
> ^
L'identificateur z n'est pas défini.
  1. Typage de exp1 (  |-  exp1 : ty1)
  2. Ajout du type obtenu à l'environnement _loc = ë (idf:ty1) <|  û
  3. Typage de exp2 dans _loc ('  |-  exp2 : ty2)
  4. L'environnement global est inchangé
(Let)
 |-  exp1 : t1     ë (idf:t1) <|  û  |-  exp2 : t2

 |-  let idf = exp1 in exp2 : t2




Le typage de CAML VI



Fonctions récursives
une fonction récursive est une expression fonctionnelle déclarée par let rec.
let rec f = function x -> corps
#let rec fact1 = function n ->
     if n <= 0 then 1 
     else n * fact1 (n-1);;
fact1 : int -> int = <fun>

Typage dans
  1. Choisir deux nouvelles variables de type t1 et t2 pour désigner le type de f puis étendre ' = ë (f:t1 ® t2), (x:t1)<|  û

  2. typer corps ('  |-  corps : ty2)
  3. Faire le bilan des contraintes (t1 = t1 et t2 = t2)
(Rec)
ë (f:t1 ® t2), (x:t1)<|  û  |-  corps : t2

 |-  rec f = function x -> corps : t1 ® t2




Le typage de CAML VII



Polymorphisme
Lors du typage, certaines variables de type restent indéterminées. Le programme est très général !

#let app_tab f t =
   for i = 0 to vect_length t - 1 do
     t.(i) <- f (t.(i))
   done;;
app_tab : ('a -> 'a) -> 'a vect -> unit = 
<fun>
#let ajout n t = 
   app_tab (function x -> x+n) t;;
ajout : int -> int vect -> unit = <fun>
#let not_tab t = 
   app_tab (function x -> not x) t;;
not_tab : bool vect -> unit = <fun>

Intérêt du polymorphisme



Le typage de CAML VIII


Les variables de type non déterminées sont généralisées.
 |-  function x -> x : " a.a ® a

Schéma de type:
expression de type dont certaines variables, notées 'a, sont quantifiées universellement en tête de l'expression: " a. a ® a. Les variables non quantifiées, notées _'a, sont dites libres.


Type:
expression de type sans quantificateur.


Type monomorphe:
type ne contenant pas de variable de type


Type polymorphe:
schéma de type


Instancier un schéma de type:
remplacer les variables universellement quantifiées par une expression de type.


Généraliser un type dans :
quantifier universellement les variables du type qui ne sont pas libres dans .




Le typage de CAML IX



Typage d'une expression ne contenant que des identificateurs connus

Cas monomorphe: déjà vu.

Cas polymorphe: " a1 ... an.typ
instancier chaque ai par une nouvelle variable de type ti.

On modifie la règle de typage des variables.

(Instance)
instancier (acces (x,)) = ty

 |-  x : ty
avec:
instancier (" a1,...,an.t) = t[t1/a1,...,tn/an]

Donc, maintenant, l'environnement est une liste d'association (idf, schéma de type).




Le typage de CAML X



Exemple

#let id = function x -> x;;
id : 'a -> 'a = <fun>
#id true,id 1;;
- : bool * int = true, 1
 |-  id : " a.a ® a

 |-  id : int ® int
     |-  true : bool

 |-  id true : bool

#(function f -> f true,f 1) (function x -> x);;
Entrée interactive:
>(function f -> f true,f 1) (function x -> 
x);;
> ^
Cette expression est de type int,
mais est utilisée avec le type bool.



Le typage de CAML XI



Quand et comment généraliser ?

au moment des déclarations
let idf = exp1 in exp2

On modifie donc un peu la règle de typage des déclarations.


Expression expansive:
effectuant des effets de bord pouvant invalider le typage. En CAML, expressions de la forme e1 e2.
  1.  |-  exp1 : t1
  2. Si exp1 NON expansive, généraliser t1 en st1 = generaliser(t1), sinon st1 = t1
  3. Ajout du type obtenu à l'environnement
  4. ë (x:st1) <|  û  |-  exp2 : ty2
  5. L'environnement global est inchangé
 |-  exp1 : t1     ë (idf:st1) <|  û  |-  exp2 : t2

 |-  let idf = exp1 in exp2 : t2




Le typage de CAML XII



La généralisation
generaliser
 
 
(typ) = " t1,...,tn.typ
où les variables ti ne sont pas libres dans


Déclarations globales:
let id = exp;;

Même principe qu'auparavant mais la liaison (x:st1) est ajoutée à l'environnement global.


Exemple
#let id = function x -> x;;
id : 'a -> 'a = <fun>
#let f = id id;;
f : '_a -> '_a = <fun>
#let g = function x -> (id id) x;;
g : 'a -> 'a = <fun>



Modularité I



Un exemple

#2 + 3;;
- : int = 5
#prefix +;;
- : int -> int -> int = <fun>
##open "float";;
#prefix +;;
- : float -> float -> float = <fun>
#1.2 + 1.3 ;;
- : float = 2.5
#2 + 3;;
Entrée interactive:
>2 + 3;;
>^
Cette expression est de type int,
mais est utilisée avec le type float.



Modularité II


#add_int 2 3;;
- : int = 5
##close "float";;
#2 + 3;;
- : int = 5
#1.2 + 1.3 ;;
Entrée interactive:
>1.2 + 1.3 ;;
>^^^
Cette expression est de type float,
mais est utilisée avec le type int.



Modularité III


Un module est défini par:
  1. Un corps : définitions (types, fonctions, constantes, etc.) regroupées dans un texte nommé mod.ml.
  2. Une interface d'exportation nommée mod.mli qui définit les déclarations visibles à l'extérieur: contient des déclarations de types et de valeurs (nom, type).
  3. Pour l'importateur: module = env. de typage (utilisé au cours du typage)
  4. si aucune interface mod.mli n'est défini, toutes les déclarations sont exportées.



Modularité IV



Structure d'un environnement complet
ë Dû |´|  ë O û |´|  ëC û
  1. D : liaisons des déclarations du pgm. en cours
  2. O : liaisons des modules ouverts
  3. C : modules connus du système



Environnement initial EInit


  1. D : vide
  2. O : modules de la librairie "core" => man. de ref.
  3. C : modules connus du système



Modularité V



Construire un environnement par importation
  1. C : les liaisons déclarées dans les interfaces mod.mli connues du système sont accessibles avec la notation mod__idf.
  2. ouvrir mod: placer les liaisons de mod au début de O.
  3. Si idf de mod déjà lié dans O => masquage de la liaison antérieure, qui reste accessible avec la notation complète.
  4. Aucun masquage des liaisons de D.
Autre choix possible : pas de masquage dans O. Si conflit, notation complète (choix de Ada).




A FAIRE


Vous connaissez un langage de programmation, qui n'est pas Caml-Light. Dans ce langage :

  1. Quelle est la syntaxe des déclarations globales? Où doivent-elles être placées?
  2. Même question avec les déclarations locales.
  3. Si ce langage est typé, comment est faite la vérification/inférence des types.
  4. Si ce langage offre des bibliothèques, y a-t-il compilation séparée?
  5. Comment sont gérés les noms des bibliothèques?



Traits impératifs I


#let counter = ref 0;;
counter : int ref = ref 0
#let add_counter n =
   counter := !counter + n;;
add_counter : int -> unit = <fun>
#let afficher mess =
   print_string mess;
   print_newline ();;
afficher : string -> unit = <fun>
#exception Division_par_zero;;
L'exception Division_par_zero est 
définie.
#let division x y = 
   if y = 0 then raise Division_par_zero
   else x / y;;
division : int -> int -> int = <fun>



Traits impératifs II



Référence
(ref exp)
 |-  exp : t

 |-  ref  exp : t  ref


Affectation
(exp1 := exp2)

 |-  exp1 : t1   ref      |-  exp2 : t1

 |-  exp1 := exp2 : unit


Séquence
(exp1 ; exp2)

 |-  exp1 : t1 ;    |-  exp2 : t2

 |-  exp1;exp2 : t2


Exceptions
(exception idf of typ;;)
 |-  idf : typ ® exn
 |-  raise : " a.exn ® a




Un algorithme d'unification entre types



Un langage de types:
ty ::= ty ® ty  |  ty * ty  |  int  |  var
var est un ensemble infini de variables a1,....


Substitution:

Une substitution est une fonction s: var ® ty étendue aux types telle que:
Unification:

Deux types t1 et t2 sont unifiables, ce que l'on notera t1 º t2, s'il existe une substitution s telle que:
s(t1) = s(t2)




Un algorithme d'unification entre types


Une substitution s'écrit [a1:t1,...,an:tn]. Connaissant la substitution S1, l'unification de t1 et t2 produit la nouvelle substitution S2. On le note:
S1  |-  t1 º t2 : S2
Avec les règles:
S  |-  t º t : S

S  |-  t1 º t'1 : S1     S1  |-  t2 º t'2 : S2

S  |-  t1 ® t2 º t'1 ® t'2 : S2

S  |-  t1 º t'1 : S1     S1  |-  t2 º t'2 : S2

S  |-  t1 * t2 º t'1 * t'2 : S2

S  |-  t1 º t2 : S2     S(a) = t1

S  |-  a º t2 : S2

a ÏDom(S)     a ÏVars(S(t))

S  |-  a º t : S[a:S(t)]



Conclusion



Extensions

Partie: II
Les types de données




Plan





Introduction


Des structures: Des fonctions:


Types de base


Des collections finies d'éléments. Ils correspondent souvent à des valeurs primitives de la machine.




Produit Cartésien I



Produit cartésien d'ensembles:
A × B = { (x,y) : x Î A, yÎ B }


Projections:
A × B  ®   A, A × B  ®   B

(A × B) × C ¹ A × (B × C) mais isomorphisme


Produit cartésien de deux types:
Env  |-  e1 : t1     Env  |-  e2 : t2

Env  |-  (e1 e2) : t1*t2
#fst;;
- : 'a * 'b -> 'a = <fun>
#snd ;;
- : 'a * 'b -> 'b = <fun>

Produit cartésien de n types:

t_1 * t_2 * ..* t_n


Attention:

t1 * t2 * t3 ¹ (t1 * t2) * t3
¹ t1 * (t2 * t3)


Projections à définir par l'utilisateur




Produit cartésien II



Paires

Un constructeur de type * d'arité 2, un constructeur de valeur : la virgule ,


N-uplets

Un constructeur de type d'arité n formé de (n-1) *, un constructeur de valeur formé de (n-1) virgules ,


Définitions par cas: filtrage
#let fst = function (x,y) -> x;;
fst : 'a * 'b -> 'a = <fun>
#let snd = function (x,y) -> y;;
snd : 'a * 'b -> 'b = <fun>
#let acceder3 = function (x,y,z,t) -> z;;
acceder3 : 'a * 'b * 'c * 'd -> 'c = 
<fun>
#let double_zero = function
       (0,0) -> true
     | (x,y) -> false;;
double_zero : int * int -> bool = <fun>



Type Enregistrement I


Un type produit à composantes nommées.


Constructeur de type
donné par l'utilisateur type ... = {lab_1 : t_1 ; .., lab_n : t_n}


Constructeur de valeur
syntaxe presque identique.

#type enfant = {nom : string ; age : int};;
Le type enfant est défini.
#let simon = {nom = "Simon" ; age = 9};;
simon : enfant = {nom = "Simon"; age = 9}

Notation pointée
#let nom {nom = n;age = a} = n;;
nom : enfant -> string = <fun>
#let nom {nom = n} = n;;
nom : enfant -> string = <fun>
#let nom x = x.nom;;
nom : enfant -> string = <fun>



Enregistrements III


#let anniv = function x -> 
   {nom = x.nom ; age = x.age + 1};;
anniv : enfant -> enfant = <fun>
#(anniv simon);;
- : enfant = {nom = "Simon"; age = 10}
#simon;;
- : enfant = {nom = "Simon"; age = 9}

Masquage

#type societe = {nom : string; numero : int};;
Le type societe est défini.
#let nom2 x = x.nom;;
nom2 : societe -> string = <fun>

Règle de typage

Env  |-  e : typ     typ = {lbl1:t1;...;lbln:tn}

Env  |-  e.lbli : ti




Types somme I



Union disjointe d'ensembles:
t = A Å B = {x.g |  x Î AÈ  {y.dy Î B}


Injection canonique
On a x:t ssi
$ y1:t1 tq x = g(y1) XOR $ y2:t2 tq x = d(y2)

En Caml, ces injection sont données par l'utilisateur au moment de la définition du type.




Types somme II



Syntaxe:
type .. = C1 of t1 | C2 of t2


Convention:
C of unit Þ C

#type color = Coeur | Pique | Carreau | Trefle ;;
Le type color est défini.
#type carte = Dam of color
            | Peti of int*color
            | Jok ;;
Le type carte est défini.
Coeur, Dam, Peti constructeurs de valeur.


Prise en compte de la définition
On ajoute à l'env. global Env les liaisons
ë (C1,t1 -> som), (C2,t2 -> som)<|  Env û


Règle de typage
Env  |-  Ci : ti ® som     Env  |-  e : ti

Env  |-  Ci  e : som




Types somme III



Filtrage - Somme

#let valeurfoo = function
   | Dam _  -> 20
   | Peti (10, c) -> 10
   | Peti (x, y) -> 
       if x = 9 & y = Coeur then 11 else 0
   | Jok  -> 1 ;;
valeurfoo : carte -> int = <fun>
#type unites = V of float | H  of float;;
Le type unites est défini.
#let distance_parcourue =
   function (V x, H y) -> x *. y ;;
Entrée interactive:
> function (V x, H y) -> x *. y ;;
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Attention: ce filtrage n'est pas 
exhaustif.
distance_parcourue : unites * unites -> 
float = <fun>
#distance_parcourue (V 25.0, H 4.0);;
- : float = 100.0



Types somme IV


Les motifs M sont définis ainsi:

M ::= C  |  C  M  |  (M,...,M)
     |  true  |  false  |  i
     |  x  |  _
où:


Types somme IV



Typage des fonction définies par filtrage


Un cas
ë (x1:t1),...,(xn:tn),Env û  |-  M : typ1
ë (x1:t1),...,(xn:tn),Env û  |-  exp : typ2

Env  |-  function M -> exp :  typ1 ® typ2
où les xi sont les variables du motif M.


Plusieurs cas
Env  |-   :  function M1 -> exp1 : typ1 ® typ2
...
Env  |-   :  function Mn -> expn : typ1 ® typ2

Env  |-  function
| M1 -> exp1
...
| Mn -> expn
 : typ1 ® typ2




Types récursifs I


Un type est récursif si sa définition contient au moins une occurrence de son nom.
#type exp =   V of string 
            | C of int 
            | P of exp * exp ;;
Le type exp est défini.
#let mon_exp = P(V "x", P (V"y", C 3));;
mon_exp : exp = P (V "x", P (V "y", C 3))
#let rec eval = function 
  | V _ -> 0
  | C n  -> n 
  | P (e1, e2) -> (eval e1) + (eval e2);;
eval : exp -> int = <fun>
#let rec deriv = function s -> function
  | V nom -> if s = nom then (C 1) else (C 0)
  | C n  ->  (C 0)
  | P (e1, e2) -> P((deriv s e1),
                    (deriv s e2));;
deriv : string -> exp -> exp = <fun>



Types récursifs II


#deriv "x" mon_exp;;
- : exp = P (C 1, P (C 0, C 0))
Attention : Syntaxe correcte ¬Þ sémantique définie
#type sans_val = C of sans_val * sans_val ;; 
Le type sans_val est défini.



Conclusion



Questions

Ce document a été traduit de LATEX par HEVEA.