Algorithmique et Programmation Fonctionnelle Algorithmique et Programmation Fonctionnelle RICM3 Cours 8 : Typage, Polymorphisme, Ordre supérieur Benjamin Wack Polytech 2013 - 2014 1 / 67 Algorithmique et Programmation Fonctionnelle La dernière fois I Foncteurs I Modules I Compilation I Makefile 2 / 67 Algorithmique et Programmation Fonctionnelle Plan Généralités sur le “Typage” Typage du let et let rec Bilan Polymorphisme Vérification de types polymorphes Inférence de type polymorphe Fonctions d’ordre supérieur Conclusion 3 / 67 Algorithmique et Programmation Fonctionnelle Devoir Maison I Envoyé par mail I À rendre pour mercredi (plusieurs l’ont déjà fait) I Objectif : comprendre la différence entre deux hiérarchies de types 4 / 67 Algorithmique et Programmation Fonctionnelle Projet I Démo I Contenu : fonctions récursives, modules et foncteurs, compilation, graphisme, flots et analyse... I Fichiers fournis sur le site I Travail en binômes “officiels” I Les deux derniers TPs sont consacrés essentiellement au suivi de projet I Date limite non négociable : vendredi 20 décembre 5 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” Le problème Les messages d’erreur de typage sont parfois troublants... 7 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” Système de types I Types de données I I I simples : numériques, booléens, chaînes structurés : produits (n-uplets, records), sommes (choix) Types fonctionnels fonction = valeur ordinaire I toutes combinaisons permises, ex. listes de fonctions 8 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” Généralités sur le typage statique Objectif : rejet de programme absurdes 1 2 ou (fun x → x ) + 1 Propriétés attendues pour un système de types I Décidable I Correct I Expressif 9 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” Des exemples simples fun x -> x int -> int bool -> bool α -> α fun x -> x + 1 int -> int mais pas bool -> int 12 Pas typable, car 1 n’est pas de type ’a -> int 10 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” Un exemple moins simple fun x -> x x Pas typable, car x de type ’a -> ’a et ’a en même temps 11 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” En OCaml En OCaml les types sont inférés automatiquement. Pour les données I Types de base : int, float, char, string, bool, unit I Types construits : produits (juxtaposition) et sommes (choix) Pour les fonctions Exemple : succ int → int Types polymorphes (généricité ADA) Exemple : test d’égalité (=) α → α → bool 12 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” Typage dans un environnement Γ ` expression : type Γ environnement de typage = liste d’associations x 7→ t, où x est un nom et t un type Exemples I nbr 7→ int ` nbr + 4 : int I nbr 7→ int; b1 7→ bool ` nbr + 4 : int I nbr 7→ int; b1 7→ bool; nbr 7→ float ` nbr +. 3.14 : float 13 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” Règles initiales de typage Constantes littérales Γ ` 0 : int Et similairement pour . . . , −2, −1, 1, 2, . . ., les booléens, les flottants, les caractères, les chaînes, etc. Gestion de l’environnement de typage Γ; x 7→ t ` x : t Γ ` x :t x 6= y Γ; y 7→ u ` x : t NB. Environnement parcouru à partir de la droite 14 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” Expression conditionnelle Expression = I valeur (exemple : un entier) I if B then E1 else E2 Règle de typage Γ ` B : bool Γ ` E1 : t Γ ` E2 : t Γ ` (if B then E1 else E2 ) : t Remarques I Type calculé à la fois pour E1 et E2 (6= valeur) I Différence entre le typage statique et l’évaluation dynamique : système de types décidable 15 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” Types nommés type t = T Où t est un nom et T une expression de type Exemples I type nom = string I type test_int = int →bool On a un environnement statique supplémentaire d’associations t 7→ T Pour alléger, cet environnement est omis dans la suite, on écrira simplement déf T t = = 16 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” Typage des couples et n-uplets Construction Γ ` A:t Γ ` B:u Γ ` (A, B) : t × u Similairement pour des n-uplets Décomposition Par filtrage, cf. plus bas. 17 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” Enregistrements = produits avec champs nommés Autres langages : record ADA, struct C Il faut nommer le type type r = {c1 : T1 ;. . . cn : Tn } Accès aux champs (mathématiquement : projections) en notation pointée Exemples I type ic = { en : int ; ca : char } exemple de valeur : { en = 3 ; ca = 0 z0 } let cpl = { en = 3 ; ca = 0 z0 } in succ cpl.en .∗ 4 I type fa = { fct : int → int ; arg : int } let cp = { fct = fun x → x + 1 ; arg = 7 } in cp.fct cp.arg .∗ 8 18 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” Typage des enregistrements déf {c : T ; . . . c : T } On suppose r = 1 n n = 1 Construction Γ ` E1 : T1 . . . Γ ` En : Tn Γ ` {c1 = E1 ; . . . cn = En } : r Décomposition Pour tout i, 1 ≤ i ≤ n, Γ ` E :r Γ ` E .ci : Ti Ou par filtrage, cf. plus bas. 19 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” Fonctions A, B et E sont des expressions Notation Ocaml : fun x → E Règles de typage Γ ` A:t→u Γ ` B:t Γ ` AB :u Γ, x 7→ t ` E : u Γ ` fun x → E : t → u 20 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” Typage L’environnement Γ0 initialement chargé (appelé Pervasives) contient : I (+) 7→ int → int → int ; (−) 7→ int → int → int ; etc. I (+.) 7→ float → float → float ; (−.) 7→ float → float → float ; I (&&) 7→ bool → bool → bool ; (||) 7→ bool → bool → bool ; I voir manuel Γ0 ` (+) : int → int → int Γ0 ` 3 : int Γ0 ` (+) 3 : int → int Γ0 ` (+) 3 2 : int Γ0 ` 2 : int 21 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” Exemple Γ, x 7→ t ` E : u Γ ` fun x → E : t → u Γ ` A:t→u Γ ` B:t Γ ` AB :u Vérifier que : Γ0 ` fun x → x + 1 : int → int Γ1 ` + : int → int → int Γ1 ` 1 : int Γ1 ` +1 : int → int Γ1 ` x : int Γ1 = Γ0 , x 7→ int ` x + 1 : int Γ0 ` fun x 7→ x + 1 : int 7→ int 22 / 67 Algorithmique et Programmation Fonctionnelle Généralités sur le “Typage” Exercice Sachant que l’environnement Γ0 contient s 7→ int → int (et aucun autre couple s 7→ t) vérifier que Γ0 ` fun n→ s (s n) : int → int Notation (pour raison de place) I déf Γ , n 7→ int Γ1 = = 0 Γ1 ` s : int → int Γ0 , n 7→ int ` n : int Γ1 ` s : int → int Γ0 , n 7→ int ` s n : int Γ1 = Γ0 , n 7→ int ` s (s n) : int Γ0 ` fun n→ s (s n) : int → int 23 / 67 Algorithmique et Programmation Fonctionnelle Typage du let et let rec Typage du let Γ ` A:t Γ, x 7→ t ` B : u Γ ` let x = A in B : u Exemple # let x = 5 in x ; ; - : int = 5 Γ ` 5 : int Γ, x 7→ int ` x : int Γ ` let x = 5 in x : int 25 / 67 Algorithmique et Programmation Fonctionnelle Typage du let et let rec Exercice Γ ` A:t Γ, x 7→ t ` B : u Γ ` let x = A in B : u Vérifier le type de : # let succ = fun x -> x + 1 in succ 1 ; ; - : int = 2 val x : int -> int = <fun> .. Γ0 ` succ : int → int Γ0 ` 1 : int . Γ ` fun x 7→ x + 1 : int → int Γ0 = Γ, succ 7→ int → int ` succ 1 : int Γ ` let succ = fun x 7→ x + 1 in succ 1 : int 26 / 67 Algorithmique et Programmation Fonctionnelle Typage du let et let rec Typage du let rec Γ, x 7→ t ` A : t Γ, x 7→ t ` B : u Γ ` let rec x = A in B : u Exemple : fact # let rec fact n = if n = 0 then 1 else n* (fact (n-1)) ; ; val fact : int -> int = <fun> Au tableau en utilisant Γ, x 7→ t ` A : t Γ, x 7→ t ` B : u Γ ` let rec x = A in B : u 27 / 67 Algorithmique et Programmation Fonctionnelle Typage du let et let rec Typage du filtrage (match) type t = C1 of T1,1 × . . . T1,n1 | . . . | Cp of Tp,1 × . . . Tp,np Posons M = match E with .. . |Ci (x1 , . . . xni ) → Ei .. . où chaque expression Ei dépend de x1 . . . xni . E :t ∀i, 1 ≤ i ≤ p, Γ; x1 → 7 Tp,1 . . . xni 7→ Tp,ni ` Ei : T Γ ` M:T 28 / 67 Algorithmique et Programmation Fonctionnelle Typage du let et let rec Typage du match Exemple : len # let rec len l = match l with | Nil -> 0 | Cons(h, t) -> 1 + (len t) ; ; val len : liste -> int = <fun> A faire en utilisant le typage de let rec et du match Γ, x 7→ t ` A : t Γ, x 7→ t ` B : u Γ ` let rec x = A in B : u 29 / 67 Algorithmique et Programmation Fonctionnelle Bilan En somme, qu’est-ce qu’un type ? Moyens de construction élémentaires : (principes d’introduction) Indiquent les valeurs du type (infos non réductibles) I produits (et enregistrements) I sommes I exponentielles (fonctions, tableaux) Procédés d’utilisation élémentaires : (principes d’élimination) I produits (projections, champs) I sommes (filtrage) I exponentielles (application, accès) Symétrie introduction / élimination 31 / 67 Algorithmique et Programmation Fonctionnelle Bilan Que fait-on d’un système de types ? I I I I Vérification pure (type partout) fun (x :int) -> (+ : int -> int -> int) (3 :int) (x :int) : int Déclaration des fonctions et variables locales et propagation des types fun (x :int) -> let (y : int) = 3 in (+) y x Déclaration des fonctions et propagation des types fun (x :int) -> let y = 3 in (+) y x Inférence complète fun x -> let y = 3 in (+) y x Propriétés I Correction (succès de l’inférence ⇒ terme typable) I Complétude (terme typable ⇒ succès de l’inférence) 32 / 67 Algorithmique et Programmation Fonctionnelle Bilan Qui guide l’inférence ? Γ ` x : Γ(x ) Γ ` c : TC (c) Γ ` op : TC (op) Γ ` A:t Γ ` B:u Γ ` (A, B) : t × u Γ ` A:t Γ, x 7→ t ` B : u Γ ` let x = A in B : u Γ, x 7→ t ` E : u Γ ` fun x → E : t → u Γ ` A:t→u Γ ` B:t Γ ` AB :u Γ, x 7→ t ` A : t Γ, x 7→ t ` B : u Γ ` let rec x = A in B : u 33 / 67 Algorithmique et Programmation Fonctionnelle Bilan Inférence de types Principe Soit une application f k I inférer le type de k : a I inférer le type de f : nécessairement de la forme b → c I vérifier que a = b I le type inféré pour f k est c Les mauvais élèves I fun x → E demande de deviner le type de x I mais la règle d’application peut aider I let rec x = A demande de deviner le type de x ... et de A ! 34 / 67 Algorithmique et Programmation Fonctionnelle Polymorphisme Le polymorphisme : définitions Définitions I Étymologiquement : plusieurs formes I En informatique : capacité d’une fonction à « s’adapter » à des arguments de type différent Deux espèces (hors langages à objets) : I polymorphisme ad-hoc ex : + sur les entiers, les flottants, les chaînes. . . dans chaque type, le sens (le code exécuté) est différent I polymorphisme paramétrique ex : @ sur les listes d’entiers, de flottants, de chaînes. . . le code exécuté est uniformément le même 36 / 67 Algorithmique et Programmation Fonctionnelle Polymorphisme Exemples basiques * Fonction identité type : α→α let id = fun x → x Application : I à des entiers : id 3 I à des arbres : id (N (N (F, 7, F), 2, N (F, 5, F))) I à des fonctions : id (fun n → (n +3)) I à elle-même : id id, id id 3.14 Paire # let pairing x y = (x,y) ; ; type : val id : ’a -> ’b -> ’a * ’b = <fun> 37 / 67 Algorithmique et Programmation Fonctionnelle Polymorphisme Autres exemples * Ces fonctions acceptent tous les types possibles .... I ’a, ’b, ... sont des variables de types I Un type contenant des variables est dit polymorphe I Pour tout a, a donne a .... I ∀a.(a → a) 38 / 67 Algorithmique et Programmation Fonctionnelle Polymorphisme Le polymorphisme dans les données (1) Listes type α liste = Nil | Cons of α × α liste let rec app l1 l2 = match l1 with | Nil → l2 | Cons (x , l1) → Cons (x , app l1 l2) 39 / 67 Algorithmique et Programmation Fonctionnelle Polymorphisme Le polymorphisme dans les données (2) Application I à des entiers : [3 ; 0 ; 8 ] I à des arbres : [N (F, 7, F) ; F ; N (F, 5, F)] I à des fonctions : [fun n → (n +3) ; (∗) 6 ; fact] I à des fonctions polymorphes : [ (fun x → x ) ; (fun x → raise Exc) ] I à des listes : [[1 ; 2 ; 3 ] ; [1 ; 5 ] ; [] ; [7 ; 6 ; 2 ]]] Attention à l’uniformité ! 40 / 67 Algorithmique et Programmation Fonctionnelle Polymorphisme Le polymorphisme dans les données (3) Type option (en standard) type α option = None | Some of α Technique possible pour les fonctions partielles let tete l = match l with | Nil → None | Cons (x , _) → Some (x ) Type : tete : α liste → α option 41 / 67 Algorithmique et Programmation Fonctionnelle Polymorphisme Le polymorphisme ad-hoc en Ocaml Sur les opérateurs de comparaison : =, <, <=, . . . α → α → bool Disponible sur toutes les structures de données I entiers : 14 < 5 +5 ; caractères, booléens, chaînes I listes : [3 ; 4 ] < [2 ; 5 ; 7 ] ; arbres Mais pas sur les types fonctionnels, ex : α → α : id = id 42 / 67 Algorithmique et Programmation Fonctionnelle Vérification de types polymorphes Une première approche : Système F Inventé séparément par JY Girard et JC Reynolds. Les types sont : I T : type de base I α : variable type I τ1 → τ2 : type des fonctions I τ1 × τ2 : type des paires I ∀α.τ : type polymorphe Problème fun x → x x admet pour type (∀α.α → α) → (∀α.α → α) 44 / 67 Algorithmique et Programmation Fonctionnelle Vérification de types polymorphes Système de Hindley-Milner Typage de (Caml, SML, Haskell, ..) Les quantificateurs n’apparaissent qu’en tête des types. ∀α.∀β.α → β est autoisé (∀α.α) → (∀β.β) est interdit ceci rend l’inférence décidable, au contraire du système F. Les types sont : τ : := | T : type de base | α : variable type | τ1 → τ2 : type des fonctions | τ1 × τ2 : type des paires σ : := τ | ∀α.σ 45 / 67 Algorithmique et Programmation Fonctionnelle Vérification de types polymorphes Dirigé par la syntaxe t ≤ Γ(x ) Γ ` x : t t ≤ Γ(op) Γ ` op : t t ≤ TC (c) Γ ` c : t Γ ` A:t Γ, x 7→ Gen(t, Γ) ` B : u Γ ` let x = A in B : u Γ, x 7→ t ` E : u Γ ` fun x → E : t → u Γ ` A:t Γ ` B:u Γ ` (A, B) : t × u Γ ` A:t→u Γ ` B:t Γ ` AB :u Où I τ ≤ ∀α1 . . . αn .τ 0 ssi ∃τ1 , . . . , τn , τ = τ 0 [α1 ← τ1 , . . . , αn ← τn ] I Gen(t, Γ) = ∀α1 . . . αn .t où {α1 . . . αn } = L(t) \ L(Γ) 46 / 67 Algorithmique et Programmation Fonctionnelle Inférence de type polymorphe Inférence de types Principe Soit une application f k I inférer le type de k : a I inférer le type de f : nécessairement de la forme b → c I trouver la substitution σ la plus générale telle que aσ = bσ (unificateur) I le type inféré pour f k est cσ 48 / 67 Algorithmique et Programmation Fonctionnelle Inférence de type polymorphe Unification de types Exemple unifier (α → α, int → β) ? unifier (α, int); unifier (α, β) remplacer α par int ; unifier (α, β) remplacer α par int ; unifier (int, β) remplacer α par int ; unifier (β, int) remplacer α par int ; remplacer β par int ; 49 / 67 Algorithmique et Programmation Fonctionnelle Inférence de type polymorphe Inférence de types : exemples avec les mains I I fun x → x : α → α x :α 3 : int substitution : [α 7→ int] (fun x → x ) 3 : int x : α, y : β, (x , y ) : α × β (+) : int → int → int substitution : [α 7→ int] (+) x : int → int substitution : [α 7→ int] [β 7→ int] x + y : int fun (x , y ) → x + y : int ×int → int 50 / 67 Algorithmique et Programmation Fonctionnelle Inférence de type polymorphe Inférence de types : exemples (2) I fun x → g (f x ) : α1 → β2 f : α1 → β1 , g : α2 → β2 , x : α substitution : [α 7→ α1 ] f x : β1 substitution : [β1 7→ α2 ] g (f x ) : β2 fun f g x → g (f x ) : (α1 → α2 ) → (α2 → β2 ) → α1 → β2 51 / 67 Algorithmique et Programmation Fonctionnelle Inférence de type polymorphe Algorithme de typage Idée I L’analyse d’une expression produit un ensemble de contraintes (système d’équations) sur les types de chaque composante Principes I Utilisation de nouvelles variables pour les types inconnus. I Détermination des types inconnus par unification. I Propagation des types inférés. Propriétés I Correction et complétude I Principalité (type le plus général) 52 / 67 Algorithmique et Programmation Fonctionnelle Inférence de type polymorphe Exemples Quel est le type de ces fonctions ? I fun x -> (fun g -> (fun f -> f x = g x)) ; ; - : ’a -> (’a -> ’b) -> (’a -> ’b) -> bool = <fun> I fun g -> (fun f -> (fun x -> f x = g x)) ; ; - : (’a -> ’b) -> (’a -> ’b) -> ’a -> bool = <fun> 53 / 67 Algorithmique et Programmation Fonctionnelle Fonctions d’ordre supérieur zip let rec zip l1 l2 = match l1, l2 with | [], [] -> [] | h1 : : t1, h2 : : t2 -> (h1, h2) : : (zip t1 t2) | _ -> raise Longueurs_differentes 55 / 67 Algorithmique et Programmation Fonctionnelle Fonctions d’ordre supérieur power let rec power f n x = if n = 0 then x else f (power f (n-1) x) Version récursive terminale ? let rec power f n x = if n = 0 then x else power f (n-1) (f x) 56 / 67 Algorithmique et Programmation Fonctionnelle Fonctions d’ordre supérieur Juste pour chercher les ennuis... let flop f x y = f y x let rec compplus l x = match l with | [] -> x | f : : t -> f (compplus t x) 57 / 67 Algorithmique et Programmation Fonctionnelle Conclusion Aujourd’hui I Typage I Polymorphisme I Inférence de type I Ordre supérieur 59 / 67 Algorithmique et Programmation Fonctionnelle Conclusion Prochaine fois I Lambda-calcul 60 / 67 Algorithmique et Programmation Fonctionnelle Rappels exists * Écrire une fonction qui détermine si au moins un élément d’une liste vérifie une propriété. Solution # let rec exists p l = match l with [ ] -> false | x : :e -> ( p x ) || (exists p e) ; ; val exists : (’a -> bool) -> ’a list -> bool = <fun> # exists (fun x -> x > 0) [1 ;0 ;-1 ;42] ; ; - : bool = true Prédéfinie : List.exists 61 / 67 Algorithmique et Programmation Fonctionnelle Rappels forall * Écrire une fonction qui détermine si tous éléments d’une liste vérifie une propriété. Solution # let rec forall p l = match l with [ ] -> true | x : :e -> ( p x ) && (forall p e) ; ; val forall : (’a -> bool) -> ’a list -> bool = <fun> # forall (fun x -> x > -2) [1 ;0 ;-1 ;42] ; ; - : bool = true Prédéfinie : List.forall 62 / 67 Algorithmique et Programmation Fonctionnelle Rappels filter * Écrire une fonction qui filtre une liste (en utilisant append @). l1@l2=l1l2 Solution # let rec filter p l = match l with [ ] -> [ ] | x : :e -> (if p x then [x] else [ ])@(filter p e) ; ; val filter : (’a -> bool) -> ’a list -> ’a list = <fun> # filter (fun x -> x > 0) [1 ;0 ;-1 ;42] ; ; - : int list = [1 ; 42] Prédéfinie : List.filter 63 / 67 Algorithmique et Programmation Fonctionnelle Rappels Application : map Écrire une fonction qui applique une fonction à l’ensemble des termes d’une liste. Solution let rec map f = function | [ ] -> [ ] | a : : l -> let r = f a in r : : map f l # map (fun x -> x + 1) [1 ;0 ;-1 ;42] ; ; - : int list = [2 ; 1 ; 0 ; 43] Prédéfinie : List.map 64 / 67 Algorithmique et Programmation Fonctionnelle Rappels fold Écrire une fonction qui calcule la somme des éléments d’une liste d’entiers. Solution let rec somme = function | [ ] -> 0 | a : : l -> a + somme l ; ; La même en récursive terminale ? Solution let rec somme accu = function | [ ] -> accu | a : : l -> somme (accu + a) l in somme 0 ; ; 65 / 67 Algorithmique et Programmation Fonctionnelle Rappels fold * Écrire une fonction qui calcule le produit des éléments d’une liste d’entiers. Solution let rec prod accu = function | [ ] -> accu | a : : l -> prod (accu * a) l in prod 1 ; ; Écrire une fonction qui calcule la longueur d’une liste. Solution let rec longueur accu = function | [ ] -> accu | a : : l -> longueur (accu + 1) l in longueur 0 ; ; 66 / 67 Algorithmique et Programmation Fonctionnelle Rappels fold * Ecrire une fonction qui calcule le résultat d’une fonction appliquée successivement à l’ensemble des termes d’une liste. Solution let rec fold f accu = function | [ ] -> accu | a : : l -> fold f (f accu a) l ; ; let somme = fold (+) 0 ; ; let prod = fold (fun x y -> x*y) 1 ; ; let longueur = fold (fun x y -> x+1) 0 ; ; Prédéfinie : List.fold_right et List.fold_left 67 / 67