INF 321 Programmation fonctionnelle, typage, isomorphisme de

publicité
INF 321
Programmation fonctionnelle, typage,
isomorphisme de Curry-Howard
Eric Goubault
Cours 9
16 juin 2014
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
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)
4
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 8! (plus petit point fixe d’une
certaine fonctionnelle)
En Caml, c’est le let rec
6
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. TD 8)
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
8
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.
10
En fait...
On peut même se passer de l’arithmétique...(voir
exercices)
[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?
Rappel: sémantique de fix x
t
fix x t → t[fix x t/x]
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...)
12
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))
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
β-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))
15
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 )
16
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
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
(β-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.!
19
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!)
20
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)
23
Beaucoup d’ordres d’évaluation possibles!
(merci Jean-Jacques Lévy)
24
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!
25
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!)
26
Combinateur de point fixe pour l’appel par
valeur (exercice)
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)
36
Exemple: nécessité (merci JJL!)
37
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
38
Pause
Et questions? (10 minutes)
39
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
40
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 ))
41
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>
43
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))
curry en Coq
Lemma c u r r y : f o r a l l A B C : Prop , ( ( A/\B)−>C)−>A−>B−>C .
Proof .
intros A B C.
i n t r o H.
intro .
intro .
apply H.
split .
e x a c t H0 .
e x a c t H1 .
Qed .
Print curry .
curry en Coq
curry =
f u n (A B C : Prop ) (H : A/\B−>C) ( H0 : A) ( H1 : B)
=> H ( c o n j H0 H1 )
: f o r a l l A B C : Prop , (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
eval en Coq
Lemma e v a l : f o r a l l A B : Prop , ( A−>B)−>A−>B .
Proof .
i n t r o s A B.
i n t r o H.
intro .
apply H.
e x a c t H0 .
Qed .
Print eval .
48
eval en Coq
f u n (A B : Prop ) (H : A−>B) ( H0 : A)
=> H H0
: f o r a l l A B : Prop , (A−>B)−>A−>B
49
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
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 a vu 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 τ )
51
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
τ ×τ
τ →τ
α
∀α.τ
|
52
. . . 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 6!
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
56
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 synthétiser les types
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 6/poly)
59
Remarque (importante!): logique et typage...
On revient aux idées du cours 6!
Très proche de la déduction naturelle, dans un fragment de la
logique du cours 6 - 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...
60
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 6)
(⇒ 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
63
Règles de typage, suite
Affectation - (cut)?
Γ ` A Γ, A ` B
Γ`B
Rappel cours 6
(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
65
Règles de typage, suite et fin
Paire - (∧Id )?
Γ`A Γ`B
Γ`A∧B
Rappel, cours 8
(∧Id )
Γ ` A, ∆ Γ ` B, ∆
Γ ` A ∧ B, ∆
66
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)
67
C’est tout pour aujourd’hui...
La prochaine fois
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
Les langages synchrones, réseaux de Kahn
LUSTRE et la programmation réactive
Quelques conseils avant les vacances
Bon TD!
Téléchargement