Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Variants polymorphes Exemples (somme1.ml) Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Ralf Treinen type num = Z e r o l e t re c add = | ( Zero , n ) | ( S n , m) S of num ; ; function −> −> Université Paris Diderot UFR Informatique Institut de Recherche en Informatique Fondamentale let two = S [email protected] add ( two , two ) ; ; ( | S n S ( add ( n ,m ) ) ; ; ( Zero ) ) ; ; 24 novembre 2016 c Roberto Di Cosmo et Ralf Treinen Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Variants polymorphes Caractéristiques des types sommes Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Variants polymorphes Exemples (somme2.ml) type t 1 let x = type t 2 I Il faut déclarer le type avec ses constructeurs avant de les utiliser. I Un constructeur appartient à un seul type somme ; si on B;; let déclare plusieurs types somme ayant le même constructeur, seule la dernière déclaration est visible (même si OCaml fait un eort pour desambiguer les constructeurs dans certains cas). (∗ = A = B now isB2 = | B | C | B;; | C;; B;; of type function −> t r u e −> f a l s e ;; let | A | B −> −> ;; isB1 function false true isB1 = x ;; t2 ∗) Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Variants polymorphes Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Variants polymorphes Variants polymorphes, syntaxe Exemples (variant1.ml) I Les variants polymorphes sont d'autres constructeurs qui peuvent être utilisés sans avoir été déclarés préalablement dans let x = `A ; ; un type. I Syntaxiquement, on les fait précéder d'une apostrophe à l'envers ` `A true `B 4;; ;; I Un variant polymorphe : I peut appartenir à plusieurs types I I peut être utilisé avec plusieurs types d'arguments donne lieu à des types qui sont des listes de variants, avec possiblement des bornes supérieures (<) et/ou inférieures (>), et une relation de sous-typage qui est réalisée à travers le polymorphisme (d'où le nom). Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Variants polymorphes Variants polymorphes : borne inférieure I le > signie : un type qui contient au moins les constructeurs suivants ; c'est une borne inférieure qui apparaît quand on produit une valeur dont on sait qu'elle peut contenir au moins les constructeurs qui apparaissent dans le type ( `A, let `B l = 3);; [ `A types d'arguments diérents ( `B 4)];; Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Variants polymorphes Exemples (variant2.ml) let f = function | `A | `A 3 −> −> [ `A; `A 3];; ( `A, `A 5);; I la barre verticale signie ou . I Un type ne peut pas contenir le même constructeur avec des ; 1 2;; Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Variants polymorphes Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Variants polymorphes Interprétation des bornes inférieures Exemples (variant3.ml) I Une borne inférieure (ou supérieure) est un type polymorphe (∗ avec une contrainte. I [> `A | ` B] est à lire comme 'a où borne let `A ∈ 'a ∧ `B ∈ 'a I le type [> `A | ` B] est une instance, à la fois de [> `A ] (et f = | `B x | `A x superieure ∗) function −> −> x x ;; aussi de [> `B]) car `A ∈ 'a ∧ `B ∈ 'a implique `A ∈ 'a (∗ I C'est une des raisons pourquoi l'inférence de type utilise la résolution de contraintes. I Les variantes polymorphes donnent lieu à une notion de quelle let est switch = | `A | `B −> −> le type de cette fonction ? ∗) function `B `A ;; sous-typage (voir la suite). Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Variants polymorphes Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Variants polymorphes Bornes supérieures Exemples (variant4.ml) I Le < signie : un type qui peut contenir au plus les constructeurs suivants ; c'est une borne supérieure qui apparaît quand on consomme une valeur dont on sait traiter seulement les constructeurs qui apparaissent dans le type. I [< `A | ` B] est à lire comme 'a où ∀ x ∈ 'a : x = `A ∨ x = `B let f = function `S a −> 1 | `Z −> 2;; let g = function `S _ −> 4 | _ −> 5;; let h = function `S _ −> 4 | x −> f x ;; Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Variants polymorphes Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Variants polymorphes Le bornes supérieures et inférieures ensemble On peut nommer les types I On peut utiliser les types des variants dans une dénition de I f a un < parce que il n'accepte que ces deux cas. type. Un ltrage par motif fermé donne une borne supérieure. I g a un > I On ne peut pas utiliser les bornes directement dans les parce que grâce au cas _−> 5 il accepte tout type dénitions : un type avec borne, aussi appelé type ouvert qui peut contenir au moins ` S. représente un ensemble de types (tous les types compris entre Un ltrage par motif ouvert donne une borne inférieure. les bornes), et pas un seul type ; on ne peut donc pas les I h a un > nommer. comme g, mais le x doit être acceptable pour f , d'où la même borne supérieure < que pour f . I Par contre, on peut utiliser ces types ouverts dans des contraintes de type. Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Variants polymorphes Variants polymorphes Exemples (variant5.ml) type type let let let let let good = [ `A b a d = [> f = Exemples (variant6.ml) | `A : | bool function gc = ( g fcc = ( f : [< : (∗ of i n t ] ; ; ` B of i n t ] ; ; `B f u n c t i o n t r u e −> fc = ( f g = Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage −> `A `A ] bool [> `A | f a l s e −> `A | `B −> 1 | −> i n t −> [< let let −> `B | 2;; );; `A | `C ] ) `B ; ; (∗ let let (∗ `C ] ) let let lower (x : bounds [> `A y = (x : upper (x : [> `B `A bounds [< redundant | are [< = ∗) by union by intersection `A ; ; `C ] ) ; ; combined = `A ; ; `A | ` B | ` D ] ) ; ; constraints function g = (f : combined ]) `A | ` B | ` C ] ) y = (x : f = are | [ < `A `A | −> `B 0 | are | `C `B | dropped −> `D ] ∗) 1;; −> int );; ∗) Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Variants polymorphes Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Pour en savoir plus Sous-typage I Les types variant respectent une notion de sous-typage : un type variant fermé fermé Jacques Garrigue, w I Autrement dit, Chapter 4.2 of the OCaml User's Manual. v est un sous-type d'un type variant si les constructeurs de constructeurs de Polymorphic Variants. v v v sont inclus dans les w. est un sous-type de est également du type w si toute valeur de type w. I C'est grâce aux variants polymorphe qu'une valeur peut avoir plusieurs types (à part du cas de la liste vide qui est traité de façon diérente). Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Exemples (sous1.ml) type 'a type 'a vlist wlist = = Contraintes de types et sous-typage [ ` Nil [ ` Nil | | | ` Snoc of ` Cons of ` Cons of ∗ 'a 'a 'a 'a ∗ ∗ 'a 'a vlist ];; wlist wlist ] ; ; I Nous avons vu qu'on obtient une contrainte let let let 'a vlist est un s o u s −t y p e de 'a wlist ∗) (borne inférieure fonction g c alors on peut appliquer la à toutes les valeurs de type t1 . I Si t2 est un sous-type de t1 alors t2 satisfait également la x = ` Cons (42 , ` Nil ) ; ; contrainte y = ( ` Cons (42 , ` Nil ) : int vlist );; z = ( ` Cons (42 , ` Nil ) : int wlist );; g (éventuellement même un type fermé). I Si un type t1 satisfait la contrainte (∗ c et/ou supérieure) pour le type de l'argument d'une fonction c, on peut donc aussi appliquer valeurs de type t2 . g à toutes les Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Exemples (sous2.ml) Cas d'usage : constructeurs surchargés On veut plusieurs variantes d'une dénition de type, et partager les l e t re c | g = ` Nil −> | ` Cons (h , r ) | ` Snoc (h , r ) (∗ wlist let (∗ 'a −> −> satisfait x = ( ` Cons let vlist constructeurs (les types sommes ne le permettent pas). function true est y = ( ` Cons Exemple : dans la librairie Yojson, Yojson.Raw, Yojson.Safe et h=1 && ( g r) h=2 && ( g r );; les contraintes (42 , ` Nil ) un Yojson.Basic fournissent diérents sous-ensemble des constructeurs suivants : : int s o u s −t y p e (42 , ` Nil ) : de int type [ obtenues wlist ) 'a in wlist vlist ) in g pour g ∗) x ;; ∗) g y ;; Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage let let ∗) ∗) ∗) ∗) ∗) ∗) ∗) ∗) ∗) ∗) ∗) ∗) t = [ `A x = `A ; ; y = `C ; ; | Coercion et Sous-typage `B ] ; ; I On peut vouloir considérer une valeur de type ' a vlist comme une valeur de type ' a wlist . I Dans ce cas on ne sait plus que la ' a vlist ne contient pas de types compatibles I En OCaml, la coercion d'une valeur v d'un sous-type t1 du type t2 vers t2 se note (v : types vlist partout où ' a wlist est accepté. ∗) x = y ;; (∗ S R B S R B S R B S S R B S B S R B S R S R S R Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage ` Snoc, mais on peut utiliser notre ' a (∗ ] (∗ (∗ (∗ (∗ (∗ (∗ (∗ (∗ (∗ (∗ (∗ (∗ Sous-typage Exemples (sous3.ml) type | | | | | | | | | | | json = ` Assoc of ( s t r i n g ∗ j s o n ) l i s t ` Bool of b o o l ` Float of f l o a t ` F l o a t l i t of s t r i n g ` I n t of i n t ` I n t l i t of s t r i n g ` L i s t of json l i s t ` Null ` S t r i n g of s t r i n g ` S t r i n g l i t of s t r i n g ` Tuple of j s o n l i s t ` Variant of s t r i n g ∗ js on option incompatibles (x : t) = y ;; ∗) omettre le : t1). t1 :> t2). (On peut parfois Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Sous-typage Exemples (sous4.ml) type type Exemples (sous5.ml) 'a vlist = [ ` Nil | 'a wlist = [ ` Nil | | let ` Snoc wlist_of_vlist let Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage a : int vlist wlist_of_vlist l = ` Cons ` Cons of = ( l ` Cons 'a : of of ∗ 'a (1 , 'a 'a 'a ∗ ∗ 'a vlist ];; 'a wlist wlist ] ; ; vlist :> 'a wlist );; vl l vl ;; let (x : switch ( match | `A | `B :> = ( l : 'a = ( ` Cons ( 7 3 , ` N i l ) | `B ] ) int :> [> 'a vlist ]);; vlist );; = with x −> −> [ `A [ `A vlist : `B `A | `B | `C ] ) ;; Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Conversion, Contrainte, Coercion Exemples (coercion.ml) Il ne faut pas confondre ces trois concepts : I La conversion transforme une valeur d'un type en une valeur int en un float). Se fait en OCaml toujours explicitement, à l'aide des fonctions de conversion. I Une contrainte de type restreint un type, en ajoutant des équations pour des variables de type. Elle peut être utile pour des expressions qui ont un type polymorphe. I La coercion permet de considérer une valeur d'un type comme une valeur d'un type plus large. Elle peut être utile dans le contexte du sous-typage. open_vlist open_vlist ` Nil ) ; ; a ;; d'un autre type (par ex., un let let type type (∗ let let (∗ let (∗ let t1 = [ `A | `B ] ; ; t2 = [ `A | `B contraintes | `C | `D incompatibles ];; ∗) x = ( `A: t1 ) ; ; y = ( x : t2 ) ; ; coercion y = (x ` ` ouvre ' ' :> contrainte z = (x : le type t2 ) ; ; redondante [> `A ] ) ; ; ∗) ∗) Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Covariance et contravariance : produits, records, lists Covariance et contravariance : fonctions I Comme les variants polymorphes introduisent du sous-typage, il faut savoir comment ce sous-typage se propage dans le reste I Le cas des fonctions : des programmes. τ10 < τ1 I Le cas des produits : τ1 < τ10 τ1 → τ2 < τ10 → τ20 τ2 < τ20 τ1 × τ2 < τ10 × τ20 τ2 < τ20 I Le type du résultat de la fonction suit la même direction de sous-typage que le type fonctionnel (on dit qu'il est co-variant, I Le cas des enregistrements : pareil aussi noté avec un I Le cas des listes : +), alors que le type du paramètre va dans le sens inverse (on dit qu'il est contra-variant, aussi noté avec un τ1 < τ10 -). τ1 list < τ10 list Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage D'où vient la contravariance ? I Étant donnée une fonction Exemples (sous6.ml) f : τ1 → τ2 quelconque. I Quand peut-on aussi la considérer comme une fonction τ0 → τ0 ? 1 2 1. Il faut que le résultat de f soit aussi du type τ20 : τ2 < τ20 2. Il faut que toute valeur de type τ10 soit acceptée comme argument à la fonction f : τ10 < τ1 let f : [ `T let g : [ | `T ] (∗ le type (f :> [ (∗ le type de (g :> [ | −> de f −> `T ] `T `Z ] −> int int est = un = function function s o u s −t y p e `T `T −> du −> 4 1 | `Z de g ∗) −> 2;; ;; type int );; g `Z ] n ' est −> pas int );; un s o u s −t y p e du type de f ∗) Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Les annotations de variance sur les types abstraits I Il est important de connaître la variance des paramètres des fonctions, pour savoir déterminer si un type fonctionnel est sous-type d'un autre. Exemples (abstrait1.ml) module type type + ' a end ; ; POS = sig t I Le compilateur détermine cette information pour tous les types qu'il connaît, mais il ne peut pas le faire pour les types abstraits dans les interfaces des modules. type 'a t I C'est le programmeur qui peut indiquer la variance avec un + ou un - type type +' a t (−'a ) tt Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage module type NEG type − ' a t end ; ; (∗ Erreur ( ∗ OK module type end ; ; module type end ; ; (∗ ∗) 'a Erreur module type end ; ; struct P : POS = 'a∗'a t = ∗) struct N : POS = 'a ' a−>b o o l t = Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Exemples (abstrait2.ml) module type end ; ; ( ∗ OK sig ∗) P : NEG = 'a = Exemples (sous8.ml) t = struct let f1 = fun ( ` Nombre x) −> x = 0;; let f2 = fun ( ` Nombre x) −> x = 0.0;; 'a∗'a (∗ let ∗) N : NEG = 'a t = struct ' a−>b o o l Attention f x = f1 : type x || f2 non x ;; habite ! ∗) Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Types non habités Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Résumé des variants polymorphes I Les variants polymorphes permettent d'écrire des programmes I Dans l'exemple du transparent précédent : La dénition de f force le type de l'argument du constructeur ` Nombre a être un int et un oat à la fois ( int & oat ). I Dans cet exemple, cette contrainte est impossible à satisfaire. plus exibles que les types sommes habituels. I Mais ils ont leurs inconvénients : I I f est donc bien typée, mais on ne pourra jamais l'appliquer car il y a aucune valeur de ce type. I ils induisent des problèmes de typage parfois complexes moins de vérications sont faites statiquement, et il devient facile d'utiliser un constructeur qui n'existe pas, ou avec un mauvais type, sans que le compilateur ne dise rien ils induisent une petite perte d'ecacité des programmes. I Donc : à utiliser seulement si on peut en tirer prot. Programmation Fonctionnelle Avancée 8 : Utilisation avancée du system de typage : variants polymorphes et sous-typage Sous-typage Pour en savoir plus Jacques Garrigue. Code reuse through polymorphic variants. In Workshop on Foundations of Software Engineering, 2000. http://www.math.nagoya-u.ac.jp/ ~garrigue/papers/fose2000.html. Available from