Glace vanille imp ´erative
1. S ´eparer les jaunes et les blancs de 2 œufs 2. Garder les blancs au r ´efrig ´erateur!
3. Ajouter
½
tasse de sucre aux jaunes!4. Y ajouter les graines d’une (voire 1
½
) gousse de vanille!5. Brasser vigoureusement (c¸a devient un peu blanch ˆatre)!
6. Battre 2dl de cr `eme!
7. Battre les blancs en neige!
8. M ´elanger le tout et mettre au cong ´elateur!
Glace vanille fonctionnelle
1 gousse 2 œufs
↓ ↓
100g Sucre Extraire S ´eparer 2dl cr `eme
& ↓ . & ↓
Brasser Battre Battre
& ↓ .
M ´elanger
↓
Congeler
Lambda-calcul
Invent ´e en 1941 par Alonzo Church
La base de la th ´eorie des langages de programmation Inspiration de Lisp, Scheme, ML, Haskell, ...
e ::= c | x | λx → e | e
1e
2S ´emantique:
(λx → e
1) e
2; e
1[e
2/x] β
-r ´eductionIl y a aussi le renommage-
α
et la r ´eduction-η
Chapitre 1 de Hudak, 8 et 14 de Sethi, 5 de Pierce
Haskell
Langages de la famille: Haskell, OCaml, Reason, F#, Coq, SML Utilis ´es dans l’industrie par:
Jane Street, Standard Chartered, J.P. Morgan, Facebook, BAE Systems, Cr ´edit Suisse, Microsoft, Docker, Wolfram, ...
Sucre syntaxique en Haskell
Haskell permet la notation infixe et pr ´efixe:
1 + 2
ou(+) 1 2
A l’inverse:`
mod x y
oux ‘ mod ‘ y
D ´efinition de fonction:
double
o x = o x x ⇔
double= λ o → λ x → o x x
Exemple
(∗) ‘
double‘ 7
:(λo → λx → o x x) (∗) 7 ; (λx → (∗) x x) 7 ; (∗) 7 7 ; 49
Expressions et valeurs
Une valeur est une expression irr ´eductible I.e. “pas de
β
-redex”Exemples:
2
,781
,λx → [x, x]
, ...Evaluer = r ´eduire une expression `a une valeur´
Est-ce que cela peut toujours se r ´eduire `a une valeur?
R ´ecursion
power
x 0 = 1
power
x y = x ∗ (
powerx (y − 1))
power
3 2 ; 3 ∗ (
power3 (2 − 1))
; 3 ∗ (
power3 1) ; 3 ∗ (3 ∗ (
power3 (1 − 1)))
; 3 ∗ (3 ∗ (
power3 0)) ; 3 ∗ (3 ∗ 1) ; 3 ∗ 3 ; 9
Un autre exemple: double power
3
Ordre d’ ´evaluation
¿ ¿ double
(∗) (1 + 2)
? ?Ordre d’ ´evaluation
¿ ¿ double
(∗) (1 + 2)
? ?¡Indiff ´erent!
double
(∗) (1 + 2) ;
double(∗) 3 ; 3 ∗ 3 ; 9
double
(∗) (1 + 2) ; (1 + 2) ∗ (1 + 2) ; . . . ; 9
Propri ´et ´e fondamentale d’un langage fonctionnel pur
L’ordre importe quand m ˆeme
power
x y = if y = 0 then 1 else x ∗ (
powerx (y − 1))
power
3 0
; if 0 = 0 then 1 else 3 ∗ (
power3 (0 − 1))
; if 0 = 0 then 1 else 3 ∗ (
power3 (−1))
; if 0 = 0 then 1 else 3 ∗ ( if − 1 = 0 then 1 else 3 ∗ (
power3 (−2)))
Haskell garanti qu’il termine si c’est possible
Transparence r ´ef ´erentielle
e
1 ete
2 sont strictement ´equivalentes sie
1;
∗e
3 ete
2;
∗e
3Remplacer
e
1 pare
2 dans une expressione
ne change pas le r ´esultat Modulo renommageα
, bien s ˆurC’est pour cela que l’ordre d’ ´evaluation n’importe pas
Types
Syst `eme de classification qui a deux origines ind ´ependentes:
•
Logique math ´ematique: introduits pour ´eviter des probl `emes tels que le paradoxe de Russell•
Langages de programmation: besoin de distinguer des valeurs de natures diff ´erentes (e.g. diff ´erente taille)Un type est comme un ensemble d’objets similaires Similaires = acceptent les m ˆeme op ´erations
Genres de typages
Un langage peut utiliser les types de deux mani `eres:
•
Typage dynamique: les valeurs portent leur typeN’importe quelle variable peut contenir n’importe quelle valeur
•
Typage statique: le type est associ ´e aux variablesUne variable ne peut contenir que des valeurs du type sp ´ecifi ´e Le typage est dit fort ou faible selon s’il est possible d’utiliser une op ´eration sur une valeur du mauvais type
Certains langages ne sont simplement pas typ ´es du tout!
Types en Haskell
¿ ¿
3 +
power ? ?¿ ¿ double
1 2
? ?Types en Haskell
¿ ¿
3 +
power ? ?¿ ¿ double
1 2
? ? Chaque expressione
a un typeτ
:Notation Haskell:
e :: τ
Si
v
1:: τ
etv
2:: τ
, ils peuvent ˆetre utilis ´es aux m ˆemes endroits3 :: Int
,3.14 :: Float
,True :: Bool
, power:: Int → Int → Int
Polymorphisme
Certaines fonctions peuvent avoir plusieurs types:
id i
:: Int → Int
id i
x = x
id s
:: String → String
id s
x = x
On peut alors utiliser une variable de type:
id
:: α → α
id
x = x
On dit alors que la fonction est polymorphe
Inf ´erence de types
En Haskell, il n’est pas indispensable d’ ´ecrire les types
% hugs [...]
Prelude> :type \ o x -> o x x
\o x -> o x x :: (a -> a -> b) -> a -> b Prelude>
Les annotations de type sont recommand ´ees:
•
Meilleurs messages d’erreur•
DocumentationTypes structur ´es
Paires:
(e
1, e
2)
,fst
,snd
de type:(τ
1, τ
2)
Listes:
[]
,e
h: e
t,[e
1, ..., e
n]
,head
,tail
de type:[τ ]
(1, 2) : ( Int , Int )
(1, (2, 3)) : ( Int , ( Int , Int )) (1, [2, 3]) : ( Int , [ Int ])
[1, [2, 3]] :
Error[[1], [2, 3]] : [[ Int ]]
[(1, 2), (2, 3)] : [( Int , Int )]
Exemples
length
:: [α] → Int
length
[] = 0
length
( : xs) =
lengthxs + 1
Exemples
length
:: [α] → Int
length
[] = 0
length
( : xs) =
lengthxs + 1
zip
:: ([α], [β ]) → [(α, β )]
zip
([], ) = []
zip
( , []) = []
zip
(x : xs, y : ys) = (x, y ) :
zip(xs, ys)
Exemples
length
:: [α] → Int
length
[] = 0
length
( : xs) =
lengthxs + 1
zip
:: ([α], [β ]) → [(α, β )]
zip
([], ) = []
zip
( , []) = []
zip
(x : xs, y : ys) = (x, y ) :
zip(xs, ys)
Pourquoi pas:
length
xs = if xs == [] then 0 else
length(
tailxs) + 1
Polymorphisme avec Type Classes
applymin
f x y
| x ≤ y = f x y
| otherwise = f y x
x
ety
peuvent ˆetre de typeFloat
,Int
, mais pasInt → Int
applymin
:: Ord α ⇒ (α → α → β ) → α → α → β
M ˆeme chose pour l’op ´eration d’ ´egalit ´e, l’addition, ...:
(==) :: Eq α ⇒ α → α → Bool
(+) :: Num α ⇒ α → α → α
Cr ´eer de nouveaux types
Types de donn ´ees alg ´ebriques
data
t =
Tag1τ
11. . . τ
1n|
Tag2τ
21. . . τ
2n0| . . .
|
Tagmτ
m1. . . τ
mn00Listes: data List
α =
Nil|
Consα (
Listα)
Produits: data Pair
α β =
Pairα β
Sommes: data Either
α β =
Leftα |
Rightβ
Null: data Maybe
α =
Nothing|
Justα
On peut aussi cr ´eer des alias avec type
t = τ
Exemples sur arbres
data
Treeα =
Leaf|
Node(
Treeα) α (
Treeα)
sum
::
TreeInt → Int
sum Leaf
= 0
sum
(
Nodet
ln t
r) =
sumt
l+ n +
sumt
rinsert
::
TreeInt → Int →
TreeInt
insert Leaf
n =
Node Leafn
Leaf insert(
Nodet
lm t
r) n
| n < m =
Node(
insertt
ln) m t
r| otherwise =
Nodet
lm (
insertt
rn)
Manipulation de listes
(++) :: [a] → [a] → [a]
[] ++ ys = ys
(x : xs) ++ ys = x : (xs ++ys)
Est-ce que
xs ++ [] ≡ xs
? Est-ce qu’on peut le prouver ? Qu’est-ce que cela implique ?Efficacit ´e de la concat ´enation
Combien d’op ´erations sont-elle n ´ecessaires pour ´evaluer:
([1, 2, 3] ++ [4, 5, 6]) ++ [7, 8, 9]
Qu’en est-il de
[1, 2, 3] ++ ([4, 5, 6] ++ [7, 8, 9])
Qu’elle est l’associativit ´e de
++
?Inverser les listes
reverse
:: [a] → [a]
reverse
[] = []
reverse
(x : xs) =??
R ´ep ´etition uniforme
type
Polygon= [( Int , Int )]
transX
:: Int →
Polygon→
Polygon transXo [] = []
transX
o ((x, y) : vs) = (o + x, y) :
transXo vs
toupper
[] = []
toupper
(c : cs) = let c
0= if c ≥
’a
’&& c ≤
’z
’then c −
’a
’+
’A
’else c
in c
0:
touppercs
Fonctions d’ordre sup ´erieur
Les fonctions sont des objets normaux: objets de premi `ere classe On peut les passer en argument
Les renvoyer comme valeur de retour
Les stocker dans des structures de donn ´ees
Currying
double
o x = o x x
En r ´ealit ´e:
double
≡ λo → λx → o x x
Donc
double
(+) ; λx → (+) x x
Map
map
:: (a → b) → ([a] → [b])
map
f [] = []
map
f (x : xs) = f x :
mapf xs
transX
o vs =
map(λ(x, y ) → (o + x, y)) vs
toupper
s =
map toupper’s
where
toupper’c = if c ≥
’a
’&& c ≤
’z
’then c −
’a
’+
’A
’else c
R ´eduction η
En plus de la r ´eduction
β
et de l’ ´equivalenceα
, leλ
-calcul d ´efini aussi la r ´eductionη
:λx → e x ; e
Cette r `egle n’est pas utilis ´ee aussi couramment Variantes:
( fst e, snd e) ; e
if e then
Trueelse
False; e
R ´eductions de listes
sum
:: [ Int ] → Int
sum
[] = 0
sum
(x : xs) = x +
sumxs
prod
:: [ Int ] → Int
prod
[] = 1
prod
(x : xs) = x ∗
prodxs
Un r ´educteur g ´en ´erique
foldr
:: (a → b → b) → b → [a] → b
foldr
op i [] = i
foldr
op i (x : xs) = x ‘op‘
foldrop i xs
sum
=
foldr(+) 0
prod
=
foldr(∗) 1
concat
=
foldr(++) []
Remarque (
η
-r ´eduction sur les listes): foldr(:) [] xs ≡ xs
Efficacit ´e
foldl
:: (a → b → a) → a → [b] → a
foldl
op i [] = i
foldl
op i (x : xs) =
foldlop (i ‘op‘ x) xs
flip
f x y = f y x
revcons
=
flip(:)
reverse
=
foldl revcons[]
Filtrage
•
Variable: accepte tout et le lie `a la variable•
Filtre sp ´ecial : accepte tout, ne lie rien•
Constante: accepte seulement une value de la bonne forme et seulement si les sous- ´el ´ements sont accept ´es par les sous-filtres•
Garde: expression bool ´eenne quelconque•
Filtre-OU:(f
1|f
2|f
3)
accepte une valeur ssi elle est accept ´ee parf
1,f
2, ouf
3 et lie les m ˆemes variables•
Filtre-AS:(x as f )
commef
mais en plus lie la valeur `ax
•
Filtres r ´ep ´et ´es: par exemple(x, x)
Exemple de filtre
merge
:: Ord α ⇒ ([α], [α]) → [α]
merge
((xs, [])|([], xs)) = xs
merge
(a @ (x : xs), b @ (y : ys))
| x ≤ y = x :
merge(xs, b)
| otherwise = y :
merge(a, ys)
Exemple de filtre, le retour
merge
:: Ord α ⇒ [α] → [α] → [α]
merge
xs [] = xs
merge
[] xs = xs
merge
(x : xs) (b @ (y : )) | x ≤ y = x :
mergexs b
merge
a (y : ys) = y :
mergea ys
S ´emantique statique de STLC
Γ, x : τ ` x : τ
Γ ` e
1: α → β Γ ` e
2: α Γ ` e
1e
2: β
Γ, x : τ
1` e : τ
2Γ ` λx : τ
1→ e : τ
1→ τ
2Γ ` n : Int Γ ` (+) : Int → Int → Int
Types, m ´ethodes formelles, v ´erification
V ´erification
6=
TestsM ´ethodes formelles:
•
Logique de Hoare: lourd, co ˆuteux, Sisyphe•
V ´erification de mod `ele: plus l ´eger, plus limit ´e, pas toujours formel•
G ´en ´eration automatique de code: bugs dans la spec?•
Types: tr `es l ´egers, tr `es limit ´es, tr `es formels Seuls les types sont utilis ´es `a grande ´echelle Hardware en avance sur le softwarePolymorphisme en λ -calcul
Le
λ
-calcul typ ´e avec polymorphisme s’appelle System FCœur des langages OCaml/Haskell (en th ´eorie et en pratique)
e ::= c | x | λx : τ → e | e
1e
2| Λt → e | e[τ ] τ ::= Int | τ
1→ τ
2| t | ∀t.τ
La fonction identit ´e est en fait traduite comme suit:
id
:: ∀α.α → α
id
= Λα → λx : α → x
r ´eponse
=
id[ Int ] 42
Invent ´e en 1972/1974 par Girard/Reynolds
S ´emantique statique de System F
Γ, x : τ ` x : τ
Γ ` e
1: α → β Γ ` e
2: α Γ ` e
1e
2: β
Γ, x : τ
1` e : τ
2Γ ` λx : τ
1→ e : τ
1→ τ
2Γ ` e : τ
Γ ` Λt → e : ∀t.τ
Γ ` e : ∀t.τ
1Γ ` e[τ
2] : τ
1[τ
2/t]
Curry-Howard
Intime connection entre logique et langages de programmation
Γ ` e
1: α → β Γ ` e
2: α
Γ ` e
1e
2: β
vsP
1⇒ P
2P
1P
2La r `egle de typage de l’application correspond au modus ponens La
β
-r ´eduction correspond au cut-eliminationType = Proposition; Programme = Preuve
Γ ` e : ∀t.τ
2Γ ` e[τ
1] : τ
2[τ
1/t]
vs∀x.P P [e/x]
Il n’existe pas de fonction de type
∀t
1, t
2.t
1→ t
2Noms et port ´ee
Un m ˆeme identificateur peut d ´esigner plusieurs choses Une d ´eclaration donne un sens `a un identificateur
La port ´ee d’une d ´eclaration = le r ´egion du programme o `u l’identificateur r ´ef `ere `a cette d ´eclaration
A l’inverse, les r `egles de port ´ee d ´efinissent pour chaque usage d’une` variable, `a quelle d ´eclaration il se r ´ef `ere
Chap. 5.3 de Sethi
Port ´ee lexicale
La port ´ee et dite statique ou lexicale si elle est d ´elimit ´ee textuellement La d ´eclaration correspondant `a un usage de variable est la d ´eclaration pr ´ec ´edente la plus proche dans le texte
Plusieurs d ´eclarations d’un m ˆeme identificateur peuvent ˆetre actives
let
doubleo x = o x x
f =
double(+)
g =
double(∗)
o = λo → o
in f 3 + g 4
Les droits fondamentaux des variables
Dans une expression
e
on dit qu’une variablex
est libre si elle est utilis ´ee sans ˆetre d ´efinie pare
.Expression Variables libres map
(λx → x + y) {
map, y }
(x + y, λz → z ) {x, y } (x + y, λx → x) {x, y }
Le renommage-
α
n’affecte pas les variables libresPort ´ee dynamique
La port ´ee est dite dynamique si elle est d ´elimit ´ee temporellement
La d ´eclaration correspondant `a un usage de variable est la plus r ´ecente d ´eclaration du m ˆeme identificateur encore active:
y = 1
f x = x + y
g n = (let y = f n in f y) + f (n + 1) h m = (let y = g m in g y) + g (m + 1)
Un m ˆeme usage de variable peut donc r ´ef ´erer `a diff ´erentes d ´eclarations
`a diff ´erents moments
Propri ´et ´es de la port ´ee dynamique
Acc `es direct aux variables “locales” de la fonction appelante!
Probl `eme de capture de nom:
transX (x, vs)
= map (\(a,b) -> (a + x, b)) vs
Le choix des identificateurs a de l’importance: pas de renommage-
α
! En Emacs Lisp, on utilise une convention{
pkg}
-{
name}
Pas de currying:
let f = \x -> \y -> x + y in f 1 2
Pourquoi port ´ee dynamique
let
dest=
stdout--
envoyer `a stdout par d ´efaut printfoox =
write dest(
showx)
in . . .
let
dest=
open"foobar" in
printfoo fooPassage implicite d’arguments qui changent rarement Implantation na¨ıve dans un interpr ´eteur
Pourquoi port ´ee lexicale
Permet l’analyse statique (automatique ou humaine)
⇒
M `ene `a du code plus efficace Libert ´e de choix des identificateurslet x = 3 in
foo0 + x
Est-ce correct de remplacer
x
par 3 ?Est-ce correct ensuite d’ ´eliminer la variable
x
? Fermetures, types, ordre d’ ´evaluationFermetures
λx → o x x
a besoin d’un contexte qui d ´efinio
Une fermeture associe une fonction `a son environnement:
λ
Ex → e
On distingue entre les expressions
λx → e
et les valeursλ
Ex → e (E ; λx → e) ; (E ; λ
Ex → e)
(E
2; (λ
E1x → e
1) e
2) ; (E
1, x 7→ e
2; e
1)
Seules les variables libres dans
e
sont n ´ecessaires dansE
Fermetures comme objets
Les fermetures sont des donn ´ees:
mkpair a b = \f -> if f then a else b fst p = p true
snd p = p false
La fermeture “capture” les variables libres (ici, a et b) Repr ´esentation en m ´emoire tr `es raisonnable
Ordre d’ ´evaluation
Choix principal: ´evaluer les arguments avant ou apr `es l’appel
CBV = avant appel par valeur
CBN = apr `es appel par nom
let x = f 0 in g x
o `u
g
pourrait ˆetreg x = x + x
ou au contraire
g x = 1
Structures de donn ´ees infinies
zipWith
:: (α → β → γ ) → [α] → [β ] → [γ ]
zipWith
f
xs1 xs2=
map(
uncurryf ) (
zip xs1 xs2)
zipWith
f (x : xs) (y : ys) = f x y :
zipWithf xs ys
zipWith
f = []
ones
= 1 :
onesnumbers
= 0 :
zipWith(+)
ones numbersnumbers
≡ [0, 1, 2, 3, 4, . . . ]
R ´ecursion cach ´ee
Comment utiliser la r ´ecursion avec des fonctions anonymes?
fact’ fact 0 = 1
fact’ fact n = n * fact (n - 1) fact = fact’ fact
Il semble qu’on a seulement repouss ´e le probl `eme.
Sauf que fact’ n’a pas besoin de fact mais seulement de fact’:
fact’ fact’ 0 = 1
fact’ fact’ n = n * fact’ fact’ (n - 1) fact = fact’ fact’
Maintenant, on a un probl `eme de type.
Classes de type
Programmation orient ´ee objet en Haskell Cousin des interfaces de Java:
class Drawable a where
draw :: Display → a → IO ()
Sp ´ecifie les m ´ethodes que les instances doivent fournir Peut fournir des impl ´ementation par d ´efaut
draw :: Drawable a ⇒ Display → a → IO ()
Instances de classes de type
Les instances sont d ´efinies s ´epar ´ement du type correspondant:
instance Drawable Square where
draw :: Display → Square → IO () draw s d = ...
instance Drawable Circle where
draw :: Display → Circle → IO () draw c d = ...
On peut donc les d ´efinir longtemps apr `es avoir d ´efini Square
Exemple: listes num ´eriques
instance Num a => Num [a] where x + y = zipWith (+) x y
x * y = zipWith (*) x y negate x = map negate x signum x = map signum x abs x = map abs x
fromInteger n = nlist
where nlist = fromInteger n : nlist
et ainsi:
[1, 2, 3] + 6 = ⇒ [7, 8, 9]
6 − [[1, 2, 3], [42, 43]] = ⇒ [[5, 4, 3], [−36, −37]]
Contraintes sur les types
Les classes sont des contraintes sur les types
dist :: (Ord a, Num a) => a -> a -> a
dist x y = if x > y then x - y else y - x
Une classe de type peut h ´eriter d’un (ou plusieurs) autres
classe (Ord a, Num a) => OrdNum a where ...
alors