Les r`egles de typage sont tr`es proches de la d´eduction naturelle, dans un fragment de la logique du chapitre 8. En fait, c’est une pr´esentation d’un frag-mentintuitionistepar uncalcul de s´equents. Pour s’en convaincre, oublions les termes PCF dans certaines r`egles de typages, un instant.
Reprenons la cr´eation de fonctions – (⇒Ig) : Γ, A`B Γ`A→B Rappel (chapitre 8) :
(⇒Ig) Γ`A,∆ Γ`B,∆ Γ, A⇒B`∆ Donc oui, c’est la mˆeme r`egle, avec ∆ =∅.
Maitenant l’affectation – (cut) :
Γ`A Γ, x:A`B Γ`B Rappel (chapitre 8) :
(cut) Γ`A,∆ Γ0, A`∆0 Γ,Γ0 `∆,∆0
9.7. TH ´EORIE DE LA D ´EMONSTRATION ET TYPAGE 145 Donc oui, c’est la mˆeme r`egle, avec ∆ =∅, Γ0= Γ et ∆0=B.
Revenons `a la r`egle de typage de la paire – (∧Id) : Γ`A Γ`v:B Γ`(u, v) :A∧B Rappel (chapitre 8) :
(∧Id) Γ`A,∆ Γ`B,∆ Γ`A∧B,∆
Prenons un exemple typique de cette correspondance type/formules, et preuves.
Revenons aux produits un instant.
Une fonction def :X×Y versZ peut-ˆetre consid´er´ee comme :
(i) bien sˆur une fonction qui `a un couple de valeurs (x, y), avec x∈ X et y∈Y, renvoief(x, y)∈Z;
(ii) une fonction de X versY →Z, qui `a unxdans X associe la fonction partiellefx:Y →Z telle quefx(y) =f(x, y) ;
(iii) un ´el´ement deX×Y →Z(soit une fonction de () (unit) versX×Y → Z).
Passer de (i) `a (ii) est«naturel»et on le fait constamment en OCaml. On a une fonction (d’ordre sup´erieur)
curry: ((X×Y)→Z)→(X →(Y →Z)) qui s’appelle la«curryfication».
En Caml, cela se programme de la fa¸con suivante : l e t c u r r y f x y = f ( x , y ) ; ;
v a l c u r r y : ( ’ a ∗ ’ b −> ’ c ) −> ’ a −> ’ b −> ’ c =<fun>
On a ´egalement la d´e-curryfication : l e t u n c u r r y f ( x , y ) = f x y ; ;
v a l u n c u r r y : ( ’ a −> ’ b −> ’ c ) −> ’ a ∗ ’ b −> ’ c = <fun>
Que l’on peut utiliser par exemple comme suit : 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 f 5 = g 5 ; ;
v a l f 5 : i n t −> i n t =<fun>
l e t h x = function y −> f ( x , y ) and
i = function x −> function y −> f ( x , y ) ; ; v a l h : i n t −> i n t −> i n t =<fun>
v a l i : i n t −> i n t −> i n t =<fun>
146 CHAPITRE 9. TYPAGE, ET PROGRAMMATION FONCTIONNELLE Une autre fonction«naturelle»est l’´evaluation :
eval: (X→Z)×X→Z
qui a toutxdeX, et toute fonction deX →Z associeeval(f, x) =f(x) dans Z. En Caml, cela se programme de fa¸con ´evidente :
l e t e v a l f x = f x ; ;
v a l e v a l : ( ’ a −> ’ b ) −> ’ a −> ’ b = <fun>
Vous remarquerez la similarit´e avec la logique propositionnelle. Le paradigme est celui des«proofs as programs», dans une logique«constructive»au moins :
«Programme=preuve de son type»
Illustrons cela par un exemple simple. On rappelle les fonctions Caml sui-vantes :
l e t c u r r y f x y = f ( x , y ) ; ;
v a l c u r r y : ( ’ a ∗ ’ b −> ’ c ) −> ’ a −> ’ b −> ’ c = <fun>
La fonctioncurry est en fait une«preuve» de :
((a∧b) =⇒ c) =⇒ (a =⇒ (b =⇒ c)) De mˆeme pour l’«application»qui s’´ecrit en Caml : l e t a p p l y = u n c u r r y e v a l ; ;
v a l a p p l y : ( ’ a −> ’ b ) ∗ ’ a −> ’ b =<fun>
La fonctionapply est une preuve du«modus ponens»: ((a =⇒ b)∧a) =⇒ b
Reprenons maintenant encoreapply. Supposons qu’on ait les axiomes (=« com-binateurs»)eval et uncurry: on peut en d´eduire une preuve constructive du modus ponens:
(uncurry) ((u =⇒ v) =⇒ w) =⇒ ((u∧v) =⇒ w) en faisantu= (a =⇒ b),v=a,w=bd’o`u :
(((a =⇒ b) =⇒ a) =⇒ b) =⇒ (((a =⇒ b)∧a) =⇒ b) Mais on sait par (eval) :
(eval) ((a =⇒ b) =⇒ a) =⇒ b Donc :
(uncurry eval) ((a =⇒ b)∧a) =⇒ b
Cette preuve correspond `a l’ex´ecution de la composition des fonctionsuncurry eteval :
9.8. POUR ALLER PLUS LOIN 147 u n c u r r y e v a l ; ;
− : ( ’ a −> ’ b ) ∗ ’ a −> ’ b =<fun>
De fa¸con g´en´erale, on a la correspondance de Curry-Howard. Un programme de typetest une preuve det comme suit : (en logiqueintuitioniste)
Terme logique Type informatique Implication type fonctionnel conjonction type produit
disjonction type somme
vrai typeunit
faux ⊥(exception/boucle infinie) Les quantificateurs correspondent aux typesd´ependants.
9.8 Pour aller plus loin
La plupart des cours permettant d’approfondir ce th`eme se trouvent au MPRI (en M2). Parmi les th`emes tr`es avanc´es, on retrouve :
– la g´en´eralisation de la correspondance de Curry-Howard `a la logique clas-sique (call-with-current-continuation, transformationcontinuation passing style) ;
– certains«grands»th´eor`emes ont ´et´e interpr´et´es comme des programmes (ex. th´eor`emes de compl´etude et d’incompl´etude de G¨odel, forcing de Co-hen etc. - par Jean-Louis Krivine) ;
– des liens entre la topologie alg´ebrique et certains syst`emes de types : cf.«Homotopical Foundations»de Vladimir Voevodsky (m´edaille Fields 2002),http://homotopytypetheory.orget les travaux de Steve Awodey (CMU, Pittsburgh).
Exercices
1. Calculer la s´emantique op´erationnelle du terme PCF : (fix f fun x -> ifz x then x+f(x−1)) 3 en appel par valeur.
2. Soitf et gdeux termes PCF de types : f : Np+1→N
g: Np→N
Prouver queh d´efini `a partir def et g par r´ecursion primitive (chapitre 5) peut se d´efinir en PCF. En d´eduire que PCF permet au moins de coder toutes les fonctions r´ecursives primitives.
3. D´efinir un codage des bool´eens comme on avait fait pour recoder les en-tiers au d´ebut de ce chapitre. Donner alors des termes PCF permettant d’interpr´eter les tests sur les bool´eens (sans faire appel `a ifz ).
148 CHAPITRE 9. TYPAGE, ET PROGRAMMATION FONCTIONNELLE 4. Typer le programme PCF (sch´emas monomorphe et polymorphe) :
fix f fun x -> ifz x then x+f(x−1)
Chapitre 10
Programmation r´ eactive synchrone
Ce chapitre introduit `a un paradigme de programmation original, la pro-grammation r´eactive synchrone (en particulier Lustre), ´egalement tr`es utile en pratique, par exemple pour le codage du contrˆole commande d’avions, de cen-trales nucl´eaires, etc. Le code du calculateur primaire de vol de l’A380 par exemple, est ´ecrit en Scade (Esterel Technologies), qui est une version indus-trielle de Lustre. C’est aussi un langage de programmation `a la s´emantique tr`es propre, qui permet d’illustrer encore les outils de la s´emantique du chapitre 6.
Ce langage a une belle s´emantique et a ainsi de nombreux outils associ´es, de preuve, test, etc.
Ce paradigme vient en quelque sorte d’un mariage du contrˆole et de l’informa-tique. En g´en´eral, on peut d´ecrire les programmes dans ce paradigme, graphi-quement, par sch´ema-bloc (comme Matlab/Simulink le fait par exemple) mais
´
egalement `a travers un langage textuel. Ce langage est d´eclaratif, un programme est un ensemble d’´equations, comme nous allons l’expliciter plus bas.
10.1 Lustre
Lustre est un langage de programmation d´eclaratif, donn´e par un ensemble
«d’´equations» mutuellement r´ecursives en g´en´eral, calculant des suites de si-gnaux de sortie, `a partir de signaux d’entr´ee. On peut voir la machine d’ex´ ecu-tion sous-jacente comme une machine parall`ele, dans laquelle chaque ´equation (ou «noeud») est un processus traitant des signaux cadenc´es `a un certain rythme temporel, et en renvoyant d’autres aux autres noeuds. La machine sous-jacente est donc un graphe de processus, avec un mod`ele d’ex´ecution tr`es simple : tous les processus ont la mˆeme horloge globale, c’est-`a-dire qu’ils lisent leur en-tr´ees, calculent, et produisent leurs sorties tous en mˆeme temps, `a chaque«tic» d’horloge. Lustre impl´emente en fait les r´eseaux de Kahn (on les introduit `a la section 10.4) synchrones. Par synchrone, on entend le fait qu’un message par arc
149
150 CHAPITRE 10. PROGRAMMATION R ´EACTIVE SYNCHRONE du r´eseau est envoy´e et re¸cu `a chaque «tic»de l’horloge globale. Cela permet d’´eviter l’utilisation detampons de communicationspotentiellement non born´es, avec une puissance de calcul similaire aux r´eseaux de Kahn g´en´eraux.
Un programme Lustre op`ere sur un flot, c’est-`a-dire unesuite de valeurs: une variablexen Lustre repr´esente une suite infinie de valeurs (x0, x1, . . . , xn, . . .) ; xiest la valeur dexau tempsi. Un programme Lustre prend un flot et renvoie un flot et toutes les op´erations sont globales sur un flot :
– L’´equation de flotx=eest un raccourci pour∀n, xn=en;
– L’expression arithm´etique sur les flotsx+y renvoie le flot (x0+y0, x1+ y1, . . . , xn+yn, . . .).
Lustre introduit ´egalement des op´erateurs temporels. Ceux-ci sont :
– pre (pr´ec´edent) qui donne la valeur au temps pr´ec´edent, d’un flot argu-ment :pre(x)est le flot (⊥, x0, . . . , xn−1, . . .) ;
– ->(suivi de) est utilis´e pour donner des valeurs initiales d’un flot :x->y est le flot (x0, y1, . . . , yn, . . .).
Remarquez que les flots sont typ´es.boolpar exemple est le type des flots de bool´eens.
Les expressions arithm´etiques et bool´eennes, syntaxiquement, sont les mˆemes que d’habitude, mais ´etendues point `a point aux flots. On a ´egalement une forme syntaxique pour l’affectation :let ... = ... tel, les conditionnelles :if ...
then ... else, et la s´equence.
L’organisation d’un programme Lustre est faite ainsi. Un programme Lustre est un ensemble d’´equations a priori potentiellement mutuellement r´ecursives.
Chaque ´equation est d´efini par un noeud identifi´e par le mot cl´e node. Une
´
equation ou noeud est une fonction prenant des flots en argument, renvoyant un flot en r´esultat.
Donnons pour premier exemple un programme simple, compteur d’´ev´ ene-ments :
Dans ce programme,true->resetest un flot bool´een, ´egal `a vrai `a l’instant initial et quandresetest vrai. Quand il est vrai, la valeur decountest renvoy´ee
´
egale `a z´ero. Sinon, quandevtest vrai, on renvoie la valeur `a l’instant pr´ec´edent decountplus 1 ; sinon on conserve l’ancienne valeur.
La repr´esentation graphique associ´e `a cette version textuelle du programme est la suivante :
10.1. LUSTRE 151
Voici un exemple d’utilisation de ce compteur d’´ev´enements.
mod60 = Count ( s e c o n d , minute ) ; minute = s e c o n d and p r e ( mod60 ) = 5 9 ;
Dans ce programme, mod60 est la sortie du noeud Count, qui compte les secondes, et se remet `a z´ero chaque minute.minuteest vrai quandsecondeest cadenc´e et que sa valeur pr´ec´edente est de 59.
Prenons maintenant un exemple du monde du traitement du signal : les filtres lin´eaires `a r´eponse finie. Ce sont des calculs r´ecurrents qui prennent une entr´ee `a l’instantn,xn et renvoient en sortie, `a l’instantn,yn donn´ee par :
yn=
L−1
X
m=0
h(m)xn−m
Graphiquement :
Prenons maintenant l’exemple des filtres lin´eaires `a r´eponse infinie (filtres r´ e-cursifs). Ils prennent en entr´ee `a l’instantn,xn, et renvoient en sortie `a l’instant n,yn donn´ee par :
yn =
L−1
X
m=0
b(m)xn−m+
M−1
X
m=1
a(m)yn−m
Graphiquement :
152 CHAPITRE 10. PROGRAMMATION R ´EACTIVE SYNCHRONE
Un code Lustre typique, correspondant, par exemple dans le cas o`u la sortie est donn´ee par :yn=xn+ 0.9yn−1 :
node f i l t e r ( x : r e a l ) r e t u r n s ( y : r e a l ) ; l e t
y = x +0.0 −> 0 . 9∗p r e ( y ) ; t e l ;
Prenons maintenant un autre exemple : celui du chien de garde (watchdog).
Il permet de g´erer des ´ech´eances : il ´emetalarmquandwatchdogest en attente et quedeadlineest vrai :
node WATCHDOG1( s e t , r e s e t , d e a d l i n e : b o o l ) return ( alarm : b o o l ) ; v a r w a t c h d o g i s o n : b o o l ;
l e t
alarm = d e a d l i n e and w a t c h d o g i s o n ; w a t c h d o g i s o n = f a l s e −> i f s e t t h e n t r u e
e l s e i f r e s e t t h e n f a l s e e l s e p r e ( w a t c h d o g i s o n ) ; a s s e r t n o t ( s e t and r e s e t ) :
t e l ;
(les flots bool´eenssetetresetne doivent pas ˆetre vrais en mˆeme temps).
Un des soucis principaux de la s´emantique de tels langages est d’assurer la
«causalit´e». On veut ainsi que les ´equations d´efinissant un programme aient une signification en termes de propagation d’information, et qu’en quelque sorte, celles-ci ne se mordent pas la queue. Pour assurer la causalit´e, comme tout r´eseau de Kahn se doit, on doit op´erer des restrictions syntaxiques sur les programmes Lustre. Par exemple, let x=x+1; n’est pas un programme Lustre correct : le flotx d´epend instantan´ement de lui-mˆeme, ce qui n’est pas possible (ou alors
10.2. CADENCEMENT ET«CALCUL D’HORLOGES» 153