INF 321 Programmation fonctionnelle, typage, isomorphisme de Curry-Howard Eric Goubault Cours 9 28 juin 2012 1 Dans le dernier épisode... On a vu: Validation Systèmes de preuve en logique On va voir: Retour sur la programmation fonctionnelle (Caml, Haskell...)! Stratégies d’évaluation (eager/lazy) Typage et isomorphisme de Curry-Howard En fait c’est un cours sur la récursivité, les références, et la preuve de programmes, revisitées dans les langages fonctionnels! C’est aussi l’occasion de voir une nouvelle sémantique, opérationnelle, des langages de programmation Dans le dernier épisode... On a vu: Validation Systèmes de preuve en logique On va voir: Retour sur la programmation fonctionnelle (Caml, Haskell...)! Stratégies d’évaluation (eager/lazy) Typage et isomorphisme de Curry-Howard En fait c’est un cours sur la récursivité, les références, et la preuve de programmes, revisitées dans les langages fonctionnels! C’est aussi l’occasion de voir une nouvelle sémantique, opérationnelle, des langages de programmation Mais avant cela... Comment définit-on la signature d’un modèle ou d’une théorie? C’est un ensemble de fonctions et de prédicats choisis pour une théorie ou un modèle donné Exemple donné la dernière fois: pour définir la théorie des groupes, il est naturel de prendre comme signature: fonctions: ∗ (l’opération de groupe), (l’unité du groupe) prédicats: = (égalité) −1 (l’inversion) et 1 Mais on pourrait aussi ne prendre que ∗, −1 et = (pas l’unité donc) et écrire juste en plus des axiomes classiques: ∀x, y x ∗ x −1 = y ∗ y −1 ∀x, y x ∗ (y ∗ y −1 ) = x 4 Pour les modèles... Encore plus de choix possible d’une certaine façon Exemple: les nombres réels Peut être vu comme un groupe additif, donc avec comme signature +, − (opposé, unaire) et 0 Peut être vu comme un corps, donc avec +, −, ∗ et 0 et 1 Peut être vu comme un corps totalement ordonné archimédien, donc avec +, −, ∗, 0 et 1 et comme prédicats = comme précédemment mais aussi ≤ etc. Tout dépend de ce que l’on veut “observer” 5 PCF “Programming Computable Functions” (Gordon Plotkin 1977) Substantifique moëlle des langages fonctionnels Les fonctions sont des objets “de première classe” Construction de fonction fun x -> t correspondant au Caml (au nommage près): let f x = t Application d’une fonction à un argument t t (on peut appliquer une fonction à une fonction - et même à soi-même! (“Sucre syntaxique” au dessus du λ-calcul) 6 Grammaire de PCF t ::= | | | | | | | x fun x− > t t t | t ×t n t +t | t −t | t ∗t ifz t then t else t fix x t let x = t in t | t/t Remarques On pourrait rajouter une construction somme... Le langage PCF est complet au sens de Turing! (permet de calculer toutes les fonctions récursives partielles, cf. cours 5) fix ? Opérateur de point fixe Permet de définir des fonctions récursives (interdites syntaxiquement dans PCF) Exemple, la fonction factorielle: fix f fun n -> ifz n then 1 else n ∗ (f (n − 1)) C’est la “plus petite” fonction f telle que 1 si n = 0 f (n) = n ∗ f (n − 1) sinon Sémantique donnée au cours 7! (plus petit point fixe d’une certaine fonctionnelle) En Caml, c’est le let rec 8 Sémantique opérationnelle On va décrire les actions, une à une, lors de l’exécution d’un programme PCF (sémantique petits pas) Cela va prendre la forme de règles de réduction ou de réécriture: p→q ou “le terme p se réécrit (ou se réduit en une étape) en le terme q” On a le droit de réécrire n’importe quel sous-terme, a priori dans n’importe quel ordre En fait Forme un automate (cf. taupe) Ou encore un graphe étiqueté (cf. cours 10) Règles de réduction β-réduction (fun x -> t)u → t[u/x] où t[u/x] est le terme t dans lequel on remplace syntaxiquement toutes les occurrences de la variable x par le terme u. Calcul arithmétique (tautologique!) p+q →n si l’entier p plus l’entier q est égal à n... Reste similaire... Conditionnelles ifz 0 then t else u → t ifz n then t else u → u si n 6= 0 10 Règles de réduction, suite Opérateur de point fixe fix x t → t[fix x t/x] Ce qui veut dire? Doit vous rappeler les règles de calcul de point fixe (par exemple en preuve à la Hoare) Définition let x = t in u → u[t/x] Exemples Un calcul arithmétique simple (fun x -> x + 2) 3 → 3 + 2 β-réduction → 5 règles arithmétiques Remarque: notion soulignée pour la partie qui intéragit, qui va être réduite. S’appelle un rédex. 12 En fait... On peut même se passer de l’arithmétique... [n] = fun z -> fun s -> s(s(s(. . . (s z) . . .))) (où on répète n ∈ N fois l’application de s) représente l’entier n On peut ensuite coder facilement les opérations, addition, multiplication...: + = fun n -> fun p -> fun z -> fun s -> n(pzs)s × = fun n -> fun p -> fun z -> fun s -> nz(fun z -> pzs) entiers de Church (Alonzo Church, 1930) De même pour les booléens et pour la conditionnelle... PCF: sucre syntaxique autour du λ-calcul (Church!) Terminaison des règles de réduction? Exemple fix x x → fix x x → . . . ne termine pas! En même temps, qu’est-ce que ca veut dire? (penser au typage...cf. la deuxième moitié de ce cours) Mais pratique, car en fait...du coup... Le terme fix n’est pas nécessaire non plus! (dans un cadre non-typé, voir la suite...) 14 Combinateur Y Definir le terme Y suivant: fun f -> (fun x -> f (x x))(fun x -> f (x x)) Soit g un terme PCF, alors: Y g = (fun f -> (fun x -> f (x x)) (fun x -> (f (x x))))g Combinateur Y Definir le terme Y suivant: fun f -> (fun x -> f (x x))(fun x -> f (x x)) Soit g un terme PCF, alors: Y g = (fun f -> (fun x -> f (x x)) (fun x -> (f (x x))))g β-réduction externe = (fun x -> g (x x))(fun x -> g (x x)) 16 Combinateur Y Definir le terme Y suivant: fun f -> (fun x -> f (x x))(fun x -> f (x x)) Soit g un terme PCF, alors: Y g = (fun f -> (fun x -> f (x x)) (fun x -> (f (x x))))g β-réduction externe = (fun x -> g (x x))(fun x -> g (x x)) β-réduction interne = g (fun x -> g (x x))(fun x -> g (x x)) 17 Combinateur Y Definir le terme Y suivant: fun f -> (fun x -> f (x x))(fun x -> f (x x)) Soit g un terme PCF, alors: Y g = (fun f -> (fun x -> f (x x)) (fun x -> (f (x x))))g β-réduction externe = (fun x -> g (x x))(fun x -> g (x x)) β-réduction interne = g (fun x -> g (x x))(fun x -> g (x x)) = g (Y g ) 18 Oui mais... On aurait aussi évaluer de la façon suivante: En effectuant la deuxième β-reduction (interne) avant la première... On aurait eu: Y g = (fun f -> (fun x -> (f (x x)) (fun x -> (f (x x))))) g Oui mais... On aurait aussi évaluer de la façon suivante: En effectuant la deuxième β-reduction (interne) avant la première... On aurait eu: Y g = (fun f -> (fun x -> (f(x x)) (fun x -> (f (x x)))) g (β-réduction interne) = (fun f -> (f (fun x -> f (x x))) (fun x -> f (x x))) g 20 Oui mais... On aurait aussi évaluer de la façon suivante: En effectuant la deuxième β-reduction (interne) avant la première... On aurait eu: Y g = (fun f -> (fun x -> (f (x x)) (fun x -> (f (x x))))) g (β-réduction interne) = (fun f -> (f (fun x -> f (x x))) (fun x -> f (x x))) g (β-réduction interne) = (fun f -> f f (fun x -> f (x x)) (fun x -> f (x x))) g = etc.! 21 Oui mais... On aurait aussi évaluer de la façon suivante: En effectuant la deuxième β-reduction (interne) avant la première... On aurait eu: Y g = (fun f -> (fun x -> (f (x x)) (fun x -> (f (x x))))) g (β-réduction interne) = (fun f -> (f (fun x -> f (x x))) (fun x -> f (x x))) g (β-réduction interne) = (fun f -> f f (fun x -> f (x x)) (fun x -> f (x x))) g = etc.! Ne termine pas (en fait c’est Kleene...)! (rappelez vous fix x x!) 22 Ordre d’évaluation On voit que: On n’a pas spécifié l’ordre d’utilisation des règles de réduction! Ca peut tout changer? Notre chance: Appelons terme irréductible (dans PCF) un terme sur lequel on ne peut appliquer aucune règle de réduction On a une propriété de confluence: si on utilise les règles de réduction dans n’importe quel ordre qui termine sur un terme irréductible, alors on termine toujours sur le même terme irréductible Clairement pas vrai si on oublie la condition d’irréductibilité... (penser encore à fix x x!) Ordres d’évaluation Exemple (fun f -> fun x -> f (f x))(fun x -> x + 2) fun x -> (fun x -> x + 2)(fun x -> x + 2)x fun x -> (fun x -> x + 2) (x + 2) fun x -> (fun x -> (x + 2) + 2) x fun x -> (x + 2) + 2 Beaucoup d’ordres d’évaluation possibles! (merci Jean-Jacques Lévy) 25 Beaucoup d’ordres d’évaluation possibles! (merci Jean-Jacques Lévy) 26 Passage par valeur, passage par référence, revisités Imposons un ordre d’évaluation! Ici, commençons par réduire les sous-termes les plus profonds (sans être trop formel) Cela revient à évaluer d’abord les arguments des fonctions, avant les fonctions elles-mêmes C’est le passage d’arguments par valeur! Passage par valeur: exemple (fun x -> (x + x))(2 + 3) → (fun x -> (x + x)) 5 évaluation de l’argument → 5+5 → 10 terme irréductible! 27 Passage par valeur, passage par référence, revisité Imposons un ordre d’évaluation! Evaluons les rédexs de l’extérieur vers l’intérieur Cela revient à calculer ce que l’on peut d’une fonction, sans les arguments, et de n’évaluer les arguments qu’au besoin, petit à petit... Revient à un passage par référence des arguments: on ne regarde ce qui est pointé par une référence, qu’au besoin Passage par référence: exemple Y g ! (on ne veut pas évaluer à l’intérieur de Y , pour avoir la terminaison!) 28 Combinateur de point fixe pour l’appel par valeur Peut-on quand même définir un opérateur de point fixe pour l’appel par valeur? Oui! Appel par valeur Utiliser plutôt dans ce cas le combinateur Z (dans un langage non typé...): fun f -> (fun x -> f (fun v -> ((x x)v ))) (fun x -> f (fun v -> ((x x)v ))) Autre exemple Exemple: appel par nom (fun f -> fun x -> f (f x))(fun x -> x + 2) fun x -> (fun x -> x + 2)(fun x -> x + 2) x fun x -> (fun x -> x + 2) (x + 2) fun x -> (fun x -> (x + 2) + 2) x fun x -> (x + 2) + 2 Autre exemple Exemple: appel par nom (fun f -> fun x -> f (f x))(fun x -> x + 2) fun x -> (fun x -> x + 2)(fun x -> x + 2) x fun x -> (fun x -> x + 2) (x + 2) fun x -> (fun x -> (x + 2) + 2) x fun x -> (x + 2) + 2 Autre exemple Exemple: appel par nom (fun f -> fun x -> f (f x))(fun x -> x + 2) fun x -> (fun x -> x + 2)(fun x -> x + 2) x fun x -> (fun x -> x + 2) (x + 2) fun x -> (fun x -> (x + 2) + 2) x fun x -> (x + 2) + 2 Autre exemple Exemple: appel par nom (fun f -> fun x -> f (f x))(fun x -> x + 2) fun x -> (fun x -> x + 2)(fun x -> x + 2) x fun x -> (fun x -> x + 2) (x + 2) fun x -> (fun x -> (x + 2) + 2) x fun x -> (x + 2) + 2 Autre exemple Rappel: appel par valeur (fun f -> fun x -> f (f x))(fun x -> x + 2) fun x -> (fun x -> x + 2)(fun x -> x + 2) x fun x -> (fun x -> x + 2) (x + 2) fun x -> (fun x -> (x + 2) + 2) x fun x -> (x + 2) + 2 Autre exemple Rappel: appel par valeur (fun f -> fun x -> f (f x))(fun x -> x + 2) fun x -> (fun x -> x + 2)(fun x -> x + 2) x fun x -> (fun x -> x + 2) (x + 2) fun x -> (fun x -> (x + 2) + 2) x fun x -> (x + 2) + 2 Autre exemple Rappel: appel par valeur (fun f -> fun x -> f (f x))(fun x -> x + 2) fun x -> (fun x -> x + 2)(fun x -> x + 2) x fun x -> (fun x -> x + 2) (x + 2) fun x -> (fun x -> (x + 2) + 2) x fun x -> (x + 2) + 2 Autre exemple Rappel: appel par valeur (fun f -> fun x -> f (f x))(fun x -> x + 2) fun x -> (fun x -> x + 2)(fun x -> x + 2) x fun x -> (fun x -> x + 2) (x + 2) fun x -> (fun x -> (x + 2) + 2) x fun x -> (x + 2) + 2 Evaluation paresseuse - Haskell Appel par nécessité Variante de l’appel par nom avec partage des sous-termes et des réductions correspondantes Assez proche de l’appel par nom, permet aussi de définir simplement des combinateurs de points fixe type Y Est implémenté dans le langage fonctionnel Haskell (pas Caml) Haskell langage crée en 1987 et nommé en l’honneur du logicien Haskell Curry Plus dur à compiler efficacement Plus souple pour le programmeur (exemple: structures de données infinies) 38 Exemple: nécessité (merci JJL!) 39 Nécessité et structures de données infinies Exemple: en Haskell numsFrom n = n : numsFrom ( n+1) s q u a r e s = map ( \ ˆ 2 ) ( numsfrom 0 ) t a k e 5 s q u a r e s => [ 0 , 1 , 4 , 9 , 1 6 ] Explication numsFrom n construit une liste infinie d’entiers, commençant en n square applique la fonction “carrée” sur la liste infinie (0, 1, 2, . . .) take extrait un préfixe fini: c’est l’évaluation par nécessité de ce terme qui demande juste ce qu’il faut d’évaluation de la liste infinie numsFrom 0 40 Combinateurs de point fixe en Haskell Exemple En Haskell, on peut programmer directement (bien que non nécessaire!) un combinateur de point fixe (mais pas le code de Y !), qui va terminer: y( f ) = f (y f ) f a c t f n = i f ( n == 0 ) t h e n 1 e l s e n ∗ f ( n−1) y ( f a c t ) 10 ... Remarquez les types: f : α→α y : (α → α) → (α → α) fact : (int → int) → int → int 41 En Caml? A priori On ne peut pas coder le combinateur Y à cause de l’appel par valeur (et du typage, cf. plus loin dans ce cours...), mais on peut tricher un peu... Utiliser une cloture l e t rec f i x f x = f ( fix f) x let factabs fact = function 0 −> 1 | x −> x ∗ f a c t ( x−1) let x = ( fix factabs ) 5 Remarquez les types: v a l f i x : ( ( ’ a −> ’ b ) −> ’ a −> ’ b ) −> ’ a −> ’ b = <fun > v a l f a c t a b s : ( i n t −> i n t ) −> i n t −> i n t = <fun > v a l x : i n t = 120 42 En Caml? A priori On ne peut pas coder le combinateur Y à cause de l’appel par valeur (et du typage, cf. plus loin dans ce cours...), mais on peut tricher un peu... Utiliser une cloture l e t rec f i x f x = f ( fix f) x let factabs fact = function 0 −> 1 | x −> x ∗ f a c t ( x−1) let x = ( fix factabs ) 5 Remarquez les types: v a l f i x : ( ( ’ a −> ’ b ) −> ’ a −> ’ b ) −> ’ a −> ’ b = <fun > v a l f a c t a b s : ( i n t −> i n t ) −> i n t −> i n t = <fun > v a l x : i n t = 120 43 En Caml On peut s’en sortir aussi avec des références, bien sûr, et types récursifs;... t y p e ’ a r e c c = I n o f ( ’ a r e c c −> ’ a ) l e t out ( In x ) = x l e t y f = ( f u n x a −> f ( o u t x x ) a ) ( I n ( f u n x a −> f ( o u t x x ) a ) ) En Java? Il faut être sérieusement fou... On utiliser une forme faible des clotures, en utilisant des objets JAVA (interfaces - cf. cours 4) Préliminaires... (merci à Ken Shirriff) c l a s s YFact { // i n t −> i n t interface IntFunc { int apply ( int n ) ; } // ( i n t −> i n t ) −> ( i n t −> i n t ) interface IntFuncToIntFunc { IntFunc apply ( IntFunc f ) ; }; Préliminaires... // H i g h e r −o r d e r f u n c t i o n r e t u r n i n g an i n t f u n c t i o n // F : F −> ( i n t −> i n t ) i n t e r f a c e FuncToIntFunc { I n t F u n c a p p l y ( FuncToIntFunc x ) ; } // F u n c t i o n from I n t F u n t T o I n t F u n c t o I n t F u n c // ( ( i n t −> i n t ) −> ( i n t −> i n t ) ) −> ( i n t −> i n t ) interface IntFuncToIntFuncToIntFunc { IntFunc apply ( IntFuncToIntFunc r ) ; } ; Le code JAVA de Z et de factorielle! ( new I n t F u n c T o I n t F u n c T o I n t F u n c ( ) { public IntFunc apply ( f i n a l IntFuncToIntFunc r ) { r e t u r n ( new FuncToIntFunc ( ) { p u b l i c I n t F u n c a p p l y ( f i n a l FuncToIntFunc f ) { return f . apply ( f ) ; }}) . a p p l y ( new FuncToIntFunc ( ) { p u b l i c I n t F u n c a p p l y ( f i n a l FuncToIntFunc f ) { return r . apply ( new I n t F u n c ( ) { p u b l i c i n t a p p l y ( i n t x ) { return f . apply ( f ) . apply ( x ) ; } } ) ; } } ) ; } } Code JAVA de Z et de factorielle Explication new correspond à une construction de fonction fun apply correspond à une application dans PCF (apply(p).q=p q) Utilisation Factorielle p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { System . o u t . p r i n t l n ( // Z c o m b i n a t o r ... . apply ( // R e c u r s i v e f u n c t i o n g e n e r a t o r new I n t F u n c T o I n t F u n c ( ) { public IntFunc apply ( f i n a l IntFunc f ) { r e t u r n new I n t F u n c ( ) { public int apply ( int n) { i f ( n == 0 ) r e t u r n 1 ; e l s e r e t u r n n ∗ f . a p p l y ( n −1); } } ; } } ) . apply ( // Argument Integer . parseInt ( args [ 0 ] ) ) ) ; } } 49 Utilisation Exécution > j a v a c YFact . j a v a > j a v a YFact 10 3628800 Le typage, qu’est-ce que c’est? Intérêt 1 torchon + 2 serviettes = ? (donc doit éliminer des choses comme (fun x -> x) + 1 etc.) Et finalement, pas non plus fun x -> x x ni Y ! - donc un langage typé aura a priori un combinateur de point fixe explicite! Choix dans les languages On peut avoir un langage où on déclare les types et où il y a une vérification minimale des types (Java etc.); éventuellement avec règles de transtypage (Java, C etc.): typage faible Ou un langage avec inférence de types (Caml etc.), et où ceux-ci (hors références...) assurent un bon comportement des programmes, minimal - de l’ordre de la preuve (à la Hoare, ou presque) de certaines propriétés de programme 51 Bon typage et absence de bug En quelque sorte... Le typage est une preuve de cohérence du programme, très formelle! Correspondance type et formule de logique / programme de ce type et preuve de cette formule, que l’on va voir brièvement (isomorphisme de Curry-Howard) Sûreté du typage Théorème: Si ∅ |= t : τ alors la réduction de t est infinie ou se termine sur une valeur (le |= est décrit formellement après: c’est l’inférence que le terme t a le type τ ) 52 Typage monomorphe/polymorphe Types monomorphes pour PCF τ ::= | | int τ →τ τ ×τ Types polymorphes et schémas de types On veut éviter d’avoir par exemple une fonction identité N → N, de type R → R, de type (R → R) → (R → R) etc.!: τ ::= | | | | int | bool τ ×τ τ →τ α ∀α.τ | 53 . . . types de base type produit type d’une fonction variable de type type polymorphe Environnements et jugements Environnement (abstrait!) Pour typer une expression, on a besoin de la connaissance du typage de l’environnement Env. Au lieu d’avoir Env = Var → Val, un environnement Γ associe à chaque variable x, un type Γ(x) dans notre grammaire de types On écrira souvent Γ, x : τ pour l’environnement qui vaut Γ (défini sur toutes les variables sauf x), et dans lequel x a le type τ Jugement de typage Dans l’environnement Γ, l’expression e (de PCF) a le type τ se note: Γ |= e : τ On définit un système formel comme au cours 8! Règles de typage Variables Γ |= x : Γ(x) Constantes Γ |= n : int Opérations arithmétiques Γ |= s : int Γ |= t : int Γ |= s + t : int etc. Règles de typage, suite Création de fonctions Γ, x : A |= t : B Γ |= fun x -> t : A → B Application Γ |= u : A Γ |= v : A → B Γ |= v u : B Règles de typage, suite Affectation Γ |= t : A Γ, x : A |= u : B Γ |= let x = t in u : B Conditionnelle Γ |= t : int Γ |= u : A Γ |= v : A Γ |= ifz t then u else v : A 57 Règles de typage, suite et fin Opérateur de point fixe Γ, x : A |= t : A Γ |= fix x t : A Paire Γ |= u : A Γ |= v : B Γ |= (u, v ) : A × B Exemple de typage Terme: let f = fun x -> x + 1 in f 2 ... x : int |= 1 : int x : int |= x + 1 : int ∅ |= fun x -> x + 1 : int → int ∅ |= let f = fun x -> x + 1 in f 2 : int ... f : int → int |= 2 : int Algorithme de typage Vérification de type/inférence de type On a donné des règles pour vérifier que le typage est correct On veut maintenant trouver l’existence Algorithmes Algorithme de Hindley (monomorphe) et de Damas-Milner (polymorphe - à l’origine du typage Caml): Tout terme de Caml a un type principal (le plus général) L’algorithme est fondé sur l’unification de termes du premier ordre (sorte de résolution d’équation dans une algèbre libre de termes) Complexité au pire exponentielle, en pratique quasi linéaire L’inférence de types est une forme d’inférence de preuve (cf. calcul des séquents, cours 8) Remarque (importante!): logique et typage... On revient aux idées du cours 8! Très proche de la déduction naturelle, dans un fragment de la logique du cours 8 - en fait, présentation d’un fragment intuitioniste par un calcul de séquents... Oublions les termes PCF dans certaines règles de typages, un instant... 61 Règles de typage - revisitées Création de fonctions Γ, x : A |= t : B Γ |= fun x -> t : A → B Règles de typage - revisitées Création de fonctions = (⇒ Id )? Γ, A ` B Γ`A→B Rappel (cours 8) (⇒ Id ) (donc oui, avec ∆ = ∅) Γ, A ` B, ∆ Γ ` A ⇒ B, ∆ Règles de typage, suite Affectation Γ |= t : A Γ, x : A |= u : B Γ |= let x = t in u : B 64 Règles de typage, suite Affectation - (cut)? Γ ` A Γ, A ` B Γ`B Rappel cours 8 (cut) Γ ` A, ∆ Γ0 , A ` ∆0 Γ, Γ0 ` ∆, ∆0 Donc oui, avec ∆ = ∅, Γ0 = Γ et ∆0 = B Règles de typage, suite et fin Paire Γ |= u : A Γ |= v : B Γ |= (u, v ) : A × B 66 Règles de typage, suite et fin Paire - (∧Id )? Γ`A Γ`B Γ`A∧B Rappel, cours 8 (∧Id ) Γ ` A, ∆ Γ ` B, ∆ Γ ` A ∧ B, ∆ 67 Revenons aux produits: curryfication Lien types produits/types fonctionnels Une fonction de f : X × Y vers Z peut-être considérée comme: (i) bien sûr une fonction qui à un couple de valeurs (x, y ), avec x ∈ X et y ∈ Y , renvoie f (x, y ) ∈ Z (ii) une fonction de X vers Y → Z , qui à un x dans X associe la fonction partielle fx : Y → Z telle que fx (y ) = f (x, y ) (iii) un élément de X × Y → Z (soit une fonction de () (unit) vers X × Y → Z) Passer de (i) à (ii) est “naturel” On a une fonction (d’ordre supérieur) curry : ((X × Y ) → Z ) → (X → (Y → Z )) 68 En Caml Curryfication let curry f x y = f (x , y ) ; ; v a l c u r r y : ( ’ a ∗ ’ b −> ’ c ) −> ’ a −> ’ b −> ’ c = <fun> Dé-curryfication l e t uncurry f (x , y ) = f x y ; ; v a l u n c u r r y : ( ’ a −> ’ b −> ’ c ) −> ’ a ∗ ’ b −> ’ c = <fun> Exemple l e t f ( x , y ) = x+y and g = c u r r y f ; ; v a l f : i n t ∗ i n t −> i n t = <fun> v a l g : i n t −> i n t −> i n t = <fun> l e t f5 = g 5 ; ; v a l f 5 : i n t −> i n t = <fun> let h x i = val h : val i : = function function x i n t −> i n t i n t −> i n t y −> f ( x , y ) and −> f u n c t i o n y −> f ( x , y ) ; ; −> i n t = <fun> −> i n t = <fun> Evaluation Autre fonction “naturelle” eval : (X → Z ) × X → Z qui a tout x de X , et toute fonction de X → Z associe eval(f , x) = f (x) dans Z let eval f x = f x ; ; v a l e v a l : ( ’ a −> ’ b ) −> ’ a −> ’ b = <fun> 70 Similarité avec la logique propositionnelle minimale... “Proofs as programs” Dans une logique constructive au moins: “Programme=preuve de son type” Caml - rappel let curry f x y = f (x , y ) ; ; v a l c u r r y : ( ’ a ∗ ’ b −> ’ c ) −> ’ a −> ’ b −> ’ c = <fun> La fonction curry est une preuve de: ((a ∧ b) =⇒ c) =⇒ (a =⇒ (b =⇒ c)) Autre exemple “Application” l e t apply = uncurry eval ; ; v a l a p p l y : ( ’ a −> ’ b ) ∗ ’ a −> ’ b = <fun> La fonction apply est une preuve du “modus ponens”: ((a =⇒ b) ∧ a) =⇒ b De façon générale Correspondance de Curry-Howard Un programme de type t est une preuve de t comme suit: (en logique intuitioniste) Terme logique Implication conjonction disjonction vrai faux Type informatique type fonctionnel type produit type somme type unit ⊥ (exception/boucle infinie) Les quantificateurs correspondent aux types dépendants Exemple en Caml Encore apply... Supposons qu’on ait les axiomes (=”combinateurs”) eval et uncurry: on peut en déduire une preuve constructive du modus ponens: (uncurry ) ((u =⇒ v ) =⇒ w ) =⇒ ((u ∧ v ) =⇒ w ) en faisant u = (a =⇒ b), v = a, w = b d’où: (((a =⇒ b) =⇒ a) =⇒ b) =⇒ (((a =⇒ b) ∧ a) =⇒ b) Mais on sait par (eval): (eval) ((a =⇒ b) =⇒ a) =⇒ b 74 Donc: (uncurry eval) ((a =⇒ b) ∧ a) =⇒ b Preuve correspondant à l’exécution de la composition des fonctions uncurry et eval: uncurry eval ; ; − : ( ’ a −> ’ b ) ∗ ’ a −> ’ b = <fun> 75 Pour aller plus loin Réalisabilité, systèmes de types dépendants etc. Généralisation de la correspondance de Curry-Howard à la logique classique (call-with-current-continuation, transformation continuation passing style) Certains “grands” théorèmes ont été interprétés comme des programmes (ex. théorèmes de complétude et d’incomplétude de Gödel, forcing de Cohen etc. - par Jean-Louis Krivine) Lien topologie algébrique et certains systèmes de types: cf. Vladimir Voevodsky (médaille Fields 2002) 76 C’est tout pour aujourd’hui... La prochaine fois Un peu de graphes (pour le TD, et pour introduire les réseaux de Kahn) Les langages synchrones, réseaux de Kahn LUSTRE et la programmation réactive Quelques conseils avant les vacances Bon TD! C’est tout pour aujourd’hui... La prochaine fois Un peu de graphes (pour le TD, et pour introduire les réseaux de Kahn) Les langages synchrones, réseaux de Kahn LUSTRE et la programmation réactive Quelques conseils avant les vacances Bon TD!