Types existentiels
Transcription
Types existentiels
Types abstraits de données Manipulation de domaines complexes. La manipulation est indépendante de la représentation particulière du domaine. On se donne : Un nom pour domaine abstrait Des opérations de construction d'une représentation concrète vers le domaine abstrait Des opérations de test pour les diérents constructions. Des opérations d'accès d'un objet abstrait vers les composantes d'une représentation concrète. Types existentiels 1 Exemple : exemplaire Exemple : jour Domaine : exemplaire Domaine : jour (jours vus comme des entiers de 1 à 31) Opérations de construction : Opérations de construction : cons_livre : livre → exemplaire cons_jour : {x : entier | 1 ≤ x ≤ 31} → jour cons_video : video → exemplaire Opérations de construction étendue : Opérations de test : entier_to_jour : entier → jour est_livre,est_video : exemplaire → bool Opérations d'accès : Opérations d'accès : numéro_jour : jour → {x : entier | 1 ≤ x ≤ 31} ex_livre : {x : exemplaire | est_livre(x)} → livre ex_video : {x : exemplaire | est_video(x)} → video 2 3 Exemple : Les cartes de couleur Exemple : argent Domaine : Domaine : couleur argent Constantes : Opérations de construction : pique,coeur,carreau,trèfle : couleur francs : entier → argent euros : entier → argent Opérations de test : Domaine : est_francs,est_euros : argent → bool figure Opérations d'accès : Constantes : val_argent : argent → entier as,roi,dame,valet, dix,neuf,huit,sept : figure 4 5 Domaine : main Domaine : Opérations de construction : carte cons_main : {~n ∈ carte5 | Diff(~n)} → main Opérations de construction : Opérations de construction étendue : cons_carte : couleur × figure → carte carte5 _to_main : carte5 → main Opérations d'accès : Opérations d'accès : figure_carte : carte → figure carte1 : main → carte .. . couleur_carte : carte → couleur carte5 : main → carte 6 7 Domaine : carte_tarot Constantes : Opérations d'accès : excuse : carte_tarot couleur_de_tarot : {x : carte_tarot | est_carte_simple(x)} → couleur Opérations de construction : figure_de_tarot : {x : carte_tarot | est_carte_simple(x)} cons_tarot_simple : carte → carte_tarot → figure cons_atout : {n : entier | 1 ≤ n ≤ 21} → carte_tarot atout_de_tarot : {x : carte_tarot | est_atout(x)} → Opérations de construction étendue : {n : entier | 1 ≤ n ≤ 21} entier_to_carte_tarot : entier → carte_tarot Opérations de test : est_carte_simple,est_atout : carte_tarot → bool 8 9 Forme générale d'un TDA Opérations de construction étendue : Domaine : dom1 _to_dom : d1 → dom .. . dom Constantes : domm _to_dom : dm → dom c1 , . . . , cn : dom Opérations de test : Opérations de construction : est_c1 , . . . , est_cn : dom → bool .. . cons_dom1 : {x : d1 | P1 (x)} → dom .. . est_op1 , . . . , est_opm : dom → bool cons_domm : {x : dm | Pm (x)} → dom 10 11 Quelques variantes On peut omettre ou rajouter quelques items selon les cas : Si la propriété Pi (x) est toujours true, alors on l'écrit pas. Si le domaine est construit à partir d'une seule constante ou d'une seule opération de construction, alors inutile d'écrire l'opération de test car elle est équivalente à est(x) = true. Si le domaine est construit à partir de m opérations de construction, alors m − 1 opérations de test susent. Si un domaine di est un produit cartésien a1 × . . . × ak , alors on peut remplacer l'opération d'accès Opérations d'accès : acces1 : {x : dom | est_op1 (x)} → {x : d1 | P1 (x)} .. . accesm : {x : dom | est_opm (x)} → {x : dm | Pm (x)} Autres opérations : .. . accesi : {x : dom | est_opi (x)} → {x : di | Pi (x)} par k opérations de la forme 12 13 Le Tarot en OCAML (* type couleur *) #type couleur = Pique | Coeur | Carreau | Trefle;; accesi1 : {x : dom | est_opi (x)} → .. . {y : a1 | y = proj1 (x) et x : di et Pi (x)} (* Constructeurs pour les constantes de couleur *) accesik : {x : dom | est_opi (x)} → {y : ak | y = projk (x) et x : di et Pi (x)} 14 #let #let #let #let pique coeur carreau trefle = = = = Pique;; Coeur;; Carreau;; Trefle;; 15 #let huit #let sept (* type figure *) #type figure = As | Roi | Dame | Cavalier | Valet | Dix | Neuf | Huit | Sept;; (* Constructeurs pour les constantes de figure *) #let #let #let #let #let #let #let =Huit;; =Sept;; (* type carte *) #type carte = Carte of figure * couleur;; as =As;; roi =Roi;; dame =Dame;; cavalier=Cavalier;; valet =Valet;; dix =Dix;; neuf =Neuf;; (* opération de construction du type carte *) #let cons_carte(f,c) = Carte(f,c);; 16 (* opérations d'accès du type carte *) #let figure_carte(e) = match e with Carte(f,c)->f;; #let couleur_carte(e) = match e with Carte(f,c)->c;; 17 (* opérations de construction du type carte_tarot *) #let int_to_carte_tarot (n) = if 1 <= n & n <= 21 then cons_atout (n) else failwith"numéro d'atout invalide";; (* type carte_tarot *) #type carte_tarot = Excuse | Simple of carte | Atout of int;; (* opérations de construction étendue du type carte_tarot *) (* opérations de test du type carte_tarot *) #let excuse = Excuse;; #let cons_tarot_simple(c) = Simple(c);; #let cons_atout (n) = Atout(n);; #let est_carte_simple (c) = match c with Simple(_) -> true | _ -> false;; 18 19 #let est_atout (c) = match c with Atout(_) -> true | _ -> false;; (* opérations d'accès du #let figure_de_tarot (e) = match e with Simple(Carte(f,_)) -> f | _ -> failwith "Pas une carte simple";; type carte_tarot *) #let couleur_de_tarot (e) = match e with Simple(Carte(_,c)) -> c | _ -> failwith "Pas une carte simple";; #let atout_de_tarot (e) = match e with Atout(x) -> x | _ -> failwith "Pas un atout";; 20 Les prols de symboles de la signature pique: coeur: carreau: trefle: as: roi: dame: cavalier: valet: dix: neuf: huit: -> -> -> -> -> -> -> -> -> -> -> -> 21 sept: cons_carte: figure * couleur figure_carte: carte couleur_carte: carte excuse: cons_tarot_simple: carte cons_atout: int int_to_carte_tarot:int est_carte_simple: carte_tarot est_atout: carte_tarot figure_de_tarot: carte_tarot couleur_de_tarot: carte_tarot atout_de_tarot: carte_tarot couleur couleur couleur couleur figure figure figure figure figure figure figure figure 22 -> -> -> -> -> -> -> -> -> -> -> -> -> figure carte = <fun> figure = <fun> couleur = <fun> carte_tarot carte_tarot = <fun> carte_tarot = <fun> carte_tarot = <fun> bool = <fun> bool = <fun> figure = <fun> couleur = <fun> int = <fun> 23 Codage abstrait Codage d'autres opérations d'un TDA Abstrait : on utilise uniquement les opérations abstraites dénies dans le TDA. Concret : on utilise les primitives du langage de programmation associées au domaines concrets utilisés dans le TDA. #let points (c) = if c = excuse then 4.5 else if est_carte_simple (c) then let f = figure_de_tarot(c) in if f = roi then 4.5 else if f = dame then 3.5 else if f = cavalier then 2.5 else if f = valet 24 25 Codage concret #let points(c) = match c with Excuse -> 4.5 | Simple(Carte(f,_)) -> (match f with Roi -> | Dame -> | Cavalier -> | Valet -> | _ -> | Atout(1) -> 4.5 | Atout(21) -> 4.5 | _ -> 0.5;; val points : carte_tarot -> float = <fun> then 1.5 else 0.5 else let n = atout_de_tarot(c) in if n=1 or n=21 then 4.5 else 0.5;; val points : carte_tarot -> float = <fun> 26 4.5 3.5 2.5 1.5 0.5) 27 Types existentiels Exemples simples Types : A ::= . . . | ∃α.A Expressions : M ::= . . . | pack M with A as ∃α.A p1 = pack 0 with int as ∃α.α p2 = pack (0, λx : int.x + 0) with int as ∃α.(α × (α → α)) Règle de typage pour l'introduction de ∃α.A : p3 = pack (0, λx : int.x + 0) with int as ∃α.(α × (α → int)) Γ ` M : A{α/B} p4 = pack (true, λx : bool.1) with bool as ∃α.(α × (α → int)) Γ ` pack M with B as ∃α.A : ∃α.A p5 = pack (0, λx : int.x + 0) with int as ∃α.(int × (int → int)) Le terme M donne une implémentation d'un TDA ayant signature A. Le type B utilisé dans l'implémentation du TDA est caché si α a des occurrences libres dans le type A. p1 ne sert à rien, p2 et p3 ont la même implémentation mais diérente signature, p3 et p4 ont la même signature mais diérente implémentation, p5 ne cache rien. 28 29 On peut aussi écrire : let a = 0; ; let f = λx : int.x + 0; ; let p = pack (a, f ) with int as ∃α.(a : α × (f : α → α)); ; Exemple : un TDA Ou bien let z = {a = 0; f = λx : int.x + 0}; ; let p = pack z with int as ∃α.(a : α × (f : α → α)); ; z.a et z.f sont les composantes de z . 30 31 Domaine : Exemple : une première implémentation type_pile #type pile = intlist;; Constantes : #let pile_vide = [];; val pile_vide : intlist pile_vide : type_pile Opération de construction : cons_pile : entier × type_pile → type_pile #let cons_pile(a,p) = a::p;; val cons_pile : int * intlist -> intlist = <fun> Opération de test : est_pile_nv : type_pile → bool Opérations d'accès premier_pile : {x : type_pile | est_pile_nv(x)} → entier reste_pile : {x : type_pile | est_pile_nv(x)} → type_pile #let est_pile_nv(p) = match p with a::l -> true | _ -> false;; val est_pile_nv : intlist -> bool = <fun> 32 33 type_pile = α × (int × α → α) × (α → bool) × (α → int) × (α → α) M = (pile_vide, cons_pile, est_pile_nv, #let premier_pile(p) = match p with a::l -> a;; val premier_pile : intlist -> int = <fun> premier_pile, reste_pile) type_pile{α/intlist} = intlist × (int × intlist → intlist)× #let reste_pile(p) = match p with a::l -> l;; val reste_pile : intlist -> intlist = <fun> (intlist → bool) × (intlist → int) × (intlist → intlist) Γ ` M : type_pile{α/intlist} Γ ` pack M with intlist as ∃α.type_ pile : ∃α.type_pile 34 35 Exemple : une autre implémentation # let reste_pile_nv(p) = p.reste;; val reste_pile_nv : pile_nv -> ma_pile = <fun> #type pile_nv = {tete:int; reste:ma_pile} and ma_pile = Pile_Vide | P of pile_nv;; # let pile_vide =Pile_Vide;; val pile_vide : ma_pile = Pile_Vide type pile_nv = { tete : int; reste : ma_pile; } and ma_pile = Pile_Vide | P of pile_nv # let cons_pile(a,p) = P(cons_pile_nv(a,p));; val cons_pile : int * ma_pile -> ma_pile = <fun> # let cons_pile_nv(t,r) = { tete=t ; reste = r};; val cons_pile_nv : int * ma_pile -> pile_nv = <fun> # let est_pile_nv(p) = match p with Pile_Vide -> false | _ -> true ;; val est_pile_nv : ma_pile -> bool = <fun> # let tete_pile_nv(p) = p.tete;; val tete_pile_nv : pile_nv -> int = <fun> 36 37 type_pile = # let premier_pile(p) = match p with Pile_Vide -> failwith"Pile Vide" | P(x) -> x.tete;; val premier_pile : ma_pile -> int = <fun> α × (int × α → α) × (α → bool) × (α → int) × (α → α) M = (pile_vide, cons_pile, est_pile_nv, premier_pile, reste_pile) type_pile{α/ma_pile} = ma_pile × (int × ma_pile → ma_pile)× # let reste_pile(p) = match p with Pile_Vide -> failwith"Pile Vide" | P(x) -> x.reste;; val reste_pile : ma_pile -> ma_pile = <fun> (ma_pile → bool) × (ma_pile → int) × (ma_pile → ma_pile Γ ` M : type_pile{α/ma_pile} Γ ` pack M with ma_pile as ∃α.type_ pile : ∃α.type_pile 38 39 Règle de typage pour l'élimination de ∃α.A : Γ ` M : ∃α.A Exemples simples Γ, z : A ` N : C Soit p une implémentation du type ∃α.(a : α, f : α → int) Γ ` unpack M as β with z : A in N : C unpack p as β with z : A in z.f (z.a) + 2 où la variable β n'est pas dans C unpack p as β with z : A in z.a + 2 On ouvre l'implémentation M dans le programme N en lui donnant un nom z et en supposant que le type caché est β . (unpack p as β with z : A in z.a) + 2 40 Sémantique Si p = pack M with B as ∃α.A, alors unpack p as β with y : A in N se réduit dans N {y/M }{α/β} qui se comporte comme let y : A = M in N 42 (mal typé) (mal typé) 41