Typbasierte Programmtransformation Janis Voigtländer Technische Universität Dresden 14. Juli 2009 Funktionale Programme in Haskell Ein Beispiel: map f [ ] = [] map f (a : as) = (f a) : (map f as) 1 – 2/10 Funktionale Programme in Haskell Ein Beispiel: map f [ ] = [] map f (a : as) = (f a) : (map f as) Einige Aufrufe: map succ [1, 2, 3] = [2, 3, 4] 1 – 3/10 Funktionale Programme in Haskell Ein Beispiel: map f [ ] = [] map f (a : as) = (f a) : (map f as) Einige Aufrufe: map succ [1, 2, 3] = [2, 3, 4] map not [True, False] = [False, True] 1 – 4/10 Funktionale Programme in Haskell Ein Beispiel: map f [ ] = [] map f (a : as) = (f a) : (map f as) Einige Aufrufe: map succ [1, 2, 3] = [2, 3, 4] map not [True, False] = [False, True] map even [1, 2, 3] = [False, True, False] 1 – 5/10 Funktionale Programme in Haskell Ein Beispiel: map f [ ] = [] map f (a : as) = (f a) : (map f as) Einige Aufrufe: map succ [1, 2, 3] = [2, 3, 4] map not [True, False] = [False, True] map even [1, 2, 3] = [False, True, False] map not [1, 2, 3] 1 – 6/10 Funktionale Programme in Haskell Ein Beispiel: map :: (α → β) → [α] → [β] map f [ ] = [] map f (a : as) = (f a) : (map f as) Einige Aufrufe: map succ [1, 2, 3] = [2, 3, 4] map not [True, False] = [False, True] map even [1, 2, 3] = [False, True, False] map not [1, 2, 3] 1 – 7/10 Funktionale Programme in Haskell Ein Beispiel: map :: (α → β) → [α] → [β] map f [ ] = [] map f (a : as) = (f a) : (map f as) Einige Aufrufe: map succ [1, 2, 3] = [2, 3, 4] — α, β 7→ Int, Int map not [True, False] = [False, True] — α, β 7→ Bool, Bool map even [1, 2, 3] — α, β 7→ Int, Bool = [False, True, False] map not [1, 2, 3] 1 – 8/10 Funktionale Programme in Haskell Ein Beispiel: map :: (α → β) → [α] → [β] map f [ ] = [] map f (a : as) = (f a) : (map f as) Einige Aufrufe: map succ [1, 2, 3] = [2, 3, 4] — α, β 7→ Int, Int map not [True, False] = [False, True] — α, β 7→ Bool, Bool map even [1, 2, 3] — α, β 7→ Int, Bool map not [1, 2, 3] = [False, True, False] zur Compile-Zeit zurückgewiesen 1 – 9/10 Funktionale Programme in Haskell Ein Beispiel: map :: (α → β) → [α] → [β] Einige Aufrufe: map succ [1, 2, 3] = [2, 3, 4] — α, β 7→ Int, Int map not [True, False] = [False, True] — α, β 7→ Bool, Bool map even [1, 2, 3] — α, β 7→ Int, Bool map not [1, 2, 3] = [False, True, False] zur Compile-Zeit zurückgewiesen 1 – 10/10 Komposition von Funktionen Ein weiteres Beispiel: filter :: (α → Bool) → [α] → [α] filter p [ ] = [] filter p (a : as) | p a = a : (filter p as) | otherwise = filter p as 2 – 11/14 Komposition von Funktionen Ein weiteres Beispiel: filter :: (α → Bool) → [α] → [α] filter p [ ] = [] filter p (a : as) | p a = a : (filter p as) | otherwise = filter p as Problem: Ausdrücke wie map f (filter p l ) erfordern Konstruktion von Zwischenergebnissen. 2 – 12/14 Komposition von Funktionen Ein weiteres Beispiel: filter :: (α → Bool) → [α] → [α] filter p [ ] = [] filter p (a : as) | p a = a : (filter p as) | otherwise = filter p as Problem: Ausdrücke wie map f (filter p l ) ∗ erfordern Konstruktion von Zwischenergebnissen. ∗ sum [f x | x ← [1..n], p x] sum (map f (filter p (enumFromTo 1 n))) 2 – 13/14 Komposition von Funktionen Ein weiteres Beispiel: filter :: (α → Bool) → [α] → [α] filter p [ ] = [] filter p (a : as) | p a = a : (filter p as) | otherwise = filter p as Problem: Ausdrücke wie map f (filter p l ) ∗ erfordern Konstruktion von Zwischenergebnissen. Lösung?: Explizite Regeln map f (filter p filter p (map f map f1 (map f2 filter p1 (filter p2 ∗ sum [f x | x ← [1..n], p x] l) l) l) l) ··· ··· ··· ··· sum (map f (filter p (enumFromTo 1 n))) 2 – 14/14 Komposition von Funktionen Ein weiteres Beispiel: filter :: (α → Bool) → [α] → [α] filter p [ ] = [] filter p (a : as) | p a = a : (filter p as) | otherwise = filter p as Für jede Wahl von p, f und l gilt: filter p (map f l ) = map f (filter (p ◦ f ) l ) Beweisbar per Induktion. 3 – 15/19 Komposition von Funktionen Ein weiteres Beispiel: filter :: (α → Bool) → [α] → [α] filter p [ ] = [] filter p (a : as) | p a = a : (filter p as) | otherwise = filter p as Für jede Wahl von p, f und l gilt: filter p (map f l ) = map f (filter (p ◦ f ) l ) Beweisbar per Induktion. Oder als freies Theorem“ [Wadler, FPCA’89]. ” 3 – 16/19 Komposition von Funktionen Ein weiteres Beispiel: filter :: (α → Bool) → [α] → [α] Für jede Wahl von p, f und l gilt: filter p (map f l ) = map f (filter (p ◦ f ) l ) Beweisbar per Induktion. Oder als freies Theorem“ [Wadler, FPCA’89]. ” 3 – 17/19 Komposition von Funktionen Ein weiteres Beispiel: filter :: (α → Bool) → [α] → [α] takeWhile :: (α → Bool) → [α] → [α] Für jede Wahl von p, f und l gilt: filter p (map f l ) = map f (filter (p ◦ f ) l ) takeWhile p (map f l ) = map f (takeWhile (p ◦ f ) l ) 3 – 18/19 Komposition von Funktionen Ein weiteres Beispiel: filter :: (α → Bool) → [α] → [α] takeWhile :: (α → Bool) → [α] → [α] g :: (α → Bool) → [α] → [α] Für jede Wahl von p, f und l gilt: filter p (map f l ) = map f (filter (p ◦ f ) l ) takeWhile p (map f l ) = map f (takeWhile (p ◦ f ) l ) g p (map f l ) = map f (g (p ◦ f ) l ) 3 – 19/19 Short Cut Fusion [Gill et al., FPCA’93] Schreibe Listenkonsumenten mittels foldr: : a1 foldr c n ( a2 c : a1 X ) = a2 c X : ak c [] ak n 4 – 20/22 Short Cut Fusion [Gill et al., FPCA’93] Schreibe Listenkonsumenten mittels foldr: : a1 foldr c n ( a2 c : a1 X ) = a2 c X : ak c [] ak n Schreibe Listenerzeuger mittels build: build :: (∀β. (α → β → β) → β → β) → [α] build prod = prod (:) [ ] 4 – 21/22 Short Cut Fusion [Gill et al., FPCA’93] Schreibe Listenkonsumenten mittels foldr: : a1 foldr c n ( a2 c : a1 X ) = a2 c X : ak c [] ak n Schreibe Listenerzeuger mittels build: build :: (∀β. (α → β → β) → β → β) → [α] build prod = prod (:) [ ] 4 – 22/22 Short Cut Fusion [Gill et al., FPCA’93] Jedes derart polymorphe prod muss einer Funktion der folgenden Form entsprechen, für feste k ≥ 0 und a1 , . . . , ak : c a1 prod = λc n → a2 c X c ak n 5 – 23/24 Short Cut Fusion [Gill et al., FPCA’93] Jedes derart polymorphe prod muss einer Funktion der folgenden Form entsprechen, für feste k ≥ 0 und a1 , . . . , ak : c a1 prod = λc n → a2 c X c ak n Zum Beispiel: filter :: (α → Bool) → [α] → [α] filter p as = build (λc n → let c ′ a r | p a =c ar | otherwise = r in foldr c ′ n as) 5 – 24/24 Short Cut Fusion [Gill et al., FPCA’93] Benutze folgende Regel: foldr c n (build prod) prod c n 6 – 25/31 Short Cut Fusion [Gill et al., FPCA’93] Benutze folgende Regel: foldr c ′ n′ (build prod) prod c ′ n′ 6 – 26/31 Short Cut Fusion [Gill et al., FPCA’93] Benutze folgende Regel: foldr c ′ n′ (build prod) prod c ′ n′ Zur Rechtfertigung: foldr c ′ n′ (build prod) 6 – 27/31 Short Cut Fusion [Gill et al., FPCA’93] Benutze folgende Regel: foldr c ′ n′ (build prod) prod c ′ n′ Zur Rechtfertigung: c a1 foldr c ′ n′ (build (λc n → a2 c )) X c ak n 6 – 28/31 Short Cut Fusion [Gill et al., FPCA’93] Benutze folgende Regel: foldr c ′ n′ (build prod) prod c ′ n′ Zur Rechtfertigung: : a1 foldr c ′ n′ ( a2 : ) X : ak [] 6 – 29/31 Short Cut Fusion [Gill et al., FPCA’93] Benutze folgende Regel: foldr c ′ n′ (build prod) prod c ′ n′ Zur Rechtfertigung: c′ a1 c′ a2 X c′ ak n′ 6 – 30/31 Short Cut Fusion [Gill et al., FPCA’93] Benutze folgende Regel: foldr c ′ n′ (build prod) prod c ′ n′ Zur Rechtfertigung: c′ a1 c′ a2 X c′ ak n′ Für den Compiler (GHC): {−# RULES “foldr/build” ∀(prod :: ∀β. (α → β → β) → β → β) c n. foldr c n (build prod) = prod c n #−} 6 – 31/31 Die destroy-Funktion [Svenningsson, ICFP’02] Als Alternative zu foldr: destroy :: (∀β. (β → Maybe (α, β)) → β → γ) → [α] → γ destroy cons as = cons match as wobei: data Maybe τ = Nothing | Just τ match :: [α] → Maybe (α, [α]) match [ ] = Nothing match (a : as) = Just (a, as) 7 – 32/33 Die destroy-Funktion [Svenningsson, ICFP’02] Als Alternative zu foldr: destroy :: (∀β. (β → Maybe (α, β)) → β → γ) → [α] → γ destroy cons as = cons match as wobei: data Maybe τ = Nothing | Just τ match :: [α] → Maybe (α, [α]) match [ ] = Nothing match (a : as) = Just (a, as) Zum Beispiel: zip :: [α] → [β] → [(α, β)] zip as bs = destroy (λp x → destroy (λq y → zipD p q x y ) bs) as where zipD = λp q x y → case (p x, q y ) of (Nothing , Nothing ) → [ ] (Just (a, x ′ ), Just (b, y ′ )) → (a, b) : (zipD p q x ′ y ′ ) 7 – 33/33 Eine destroy/build-Regel [V., PEPM’08] Laut Definitionen ist destroy cons (build prod ) ... 8 – 34/37 Eine destroy/build-Regel [V., PEPM’08] Laut Definitionen ist destroy cons (build prod ) das Gleiche wie cons match (prod (:) [ ]) , wobei: match [ ] = Nothing match (a : as) = Just (a, as) 8 – 35/37 Eine destroy/build-Regel [V., PEPM’08] Laut Definitionen ist destroy cons (build prod ) das Gleiche wie cons match (prod (:) [ ]) , wobei: match [ ] = Nothing match (a : as) = Just (a, as) Warum dann nicht einfach destroy cons (build prod ) cons id (prod (λa as → Just (a, as)) Nothing) ? 8 – 36/37 Eine destroy/build-Regel [V., PEPM’08] Laut Definitionen ist destroy cons (build prod ) das Gleiche wie cons match (prod (:) [ ]) , wobei: match [ ] = Nothing match (a : as) = Just (a, as) Warum dann nicht einfach destroy cons (build prod ) cons id (prod (λa as → Just (a, as)) Nothing) ? Erhält diese Regel die Semantik? 8 – 37/37 Beweis der Korrektheit Alles, was wir über cons und prod wissen, sind ihre Typen: cons :: ∀β. (β → Maybe (T1 , β)) → β → T2 und prod :: ∀β. (T1 → β → β) → β → β 9 – 38/39 Beweis der Korrektheit Alles, was wir über cons und prod wissen, sind ihre Typen: cons :: ∀β. (β → Maybe (T1 , β)) → β → T2 und prod :: ∀β. (T1 → β → β) → β → β Aber dies könnte reichen, Dank freier Theoreme! Im Folgenden, eine Beweisskizze. 9 – 39/39 Wo beginnen? Das freie Theorem für cons :: ∀β. (β → Maybe (T1 , β)) → β → T2 ist: ∀τ1 , τ2 , R ⊆ τ1 × τ2 , R strict, continuous, and bottom-reflecting. ∀p :: τ1 → Maybe (T1 , τ1 ), q :: τ2 → Maybe (T1 , τ2 ). (p 6= ⊥ ⇔ q 6= ⊥) ∧ (∀(x, y ) ∈ R. (p x, q y ) ∈ liftMaybe (lift(,) (id, R))) ⇒ ∀(z, v ) ∈ R. cons p z = cons q v 10 – 40/48 Wo beginnen? Das freie Theorem für cons :: ∀β. (β → Maybe (T1 , β)) → β → T2 ist: ∀τ1 , τ2 , R ⊆ τ1 × τ2 , R strict, continuous, and bottom-reflecting. ∀p :: τ1 → Maybe (T1 , τ1 ), q :: τ2 → Maybe (T1 , τ2 ). (p 6= ⊥ ⇔ q 6= ⊥) ∧ (∀(x, y ) ∈ R. (p x, q y ) ∈ liftMaybe (lift(,) (id, R))) ⇒ ∀(z, v ) ∈ R. cons p z = cons q v Zur Erinnerung, wir wollen beweisen: cons match (prod (:) [ ]) = cons id (prod (λa as → Just (a, as)) Nothing) 10 – 41/48 Wo beginnen? Das freie Theorem für cons :: ∀β. (β → Maybe (T1 , β)) → β → T2 ist: [Johann & V., POPL’04] ∀τ1 , τ2 , R ⊆ τ1 × τ2 , R strict, continuous, and bottom-reflecting. ∀p :: τ1 → Maybe (T1 , τ1 ), q :: τ2 → Maybe (T1 , τ2 ). (p 6= ⊥ ⇔ q 6= ⊥) ∧ (∀(x, y ) ∈ R. (p x, q y ) ∈ liftMaybe (lift(,) (id, R))) ⇒ ∀(z, v ) ∈ R. cons p z = cons q v Zur Erinnerung, wir wollen beweisen: cons match (prod (:) [ ]) = cons id (prod (λa as → Just (a, as)) Nothing) 10 – 42/48 Wo beginnen? Das freie Theorem für cons :: ∀β. (β → Maybe (T1 , β)) → β → T2 ist: [Johann & V., POPL’04] ∀τ1 , τ2 , R ⊆ τ1 × τ2 , R strict, continuous, and bottom-reflecting. ∀p :: τ1 → Maybe (T1 , τ1 ), q :: τ2 → Maybe (T1 , τ2 ). (p 6= ⊥ ⇔ q 6= ⊥) ∧ (∀(x, y ) ∈ R. (p x, q y ) ∈ liftMaybe (lift(,) (id, R))) ⇒ ∀(z, v ) ∈ R. cons p z = cons q v Zur Erinnerung, wir wollen beweisen: cons match (prod (:) [ ]) = cons id (prod (λa as → Just (a, as)) Nothing) 10 – 43/48 Wo beginnen? Das freie Theorem für cons :: ∀β. (β → Maybe (T1 , β)) → β → T2 , spezialisiert auf die Ebene von Funktionen, ist: ∀τ1 , τ2 , f :: τ1 → τ2 , f strict and total. ∀p :: τ1 → Maybe (T1 , τ1 ), q :: τ2 → Maybe (T1 , τ2 ). (p 6= ⊥ ⇔ q 6= ⊥) ∧ (∀x :: τ1 . (p x, q (f x)) ∈ liftMaybe (lift(,) (id, f ))) ⇒ ∀y :: τ1 . cons p y = cons q (f y ) Zur Erinnerung, wir wollen beweisen: cons match (prod (:) [ ]) = cons id (prod (λa as → Just (a, as)) Nothing) 10 – 44/48 Wo beginnen? Das freie Theorem für cons :: ∀β. (β → Maybe (T1 , β)) → β → T2 , spezialisiert auf die Ebene von Funktionen, ist: ∀τ1 , τ2 , f :: τ1 → τ2 , f strict and total. ∀p :: τ1 → Maybe (T1 , τ1 ), q :: τ2 → Maybe (T1 , τ2 ). (p 6= ⊥ ⇔ q 6= ⊥) ∧ (∀x :: τ1 . (p x, q (f x)) ∈ liftMaybe (lift(,) (id, f ))) ⇒ ∀y :: τ1 . cons p y = cons q (f y ) Zur Erinnerung, wir wollen beweisen: cons match (prod (:) [ ]) = cons id (prod (λa as → Just (a, as)) Nothing) 10 – 45/48 Wo beginnen? Das freie Theorem für cons :: ∀β. (β → Maybe (T1 , β)) → β → T2 , spezialisiert auf die Ebene von Funktionen, ist: ∀τ1 , τ2 , f :: τ1 → τ2 , f strict and total. ∀p :: τ1 → Maybe (T1 , τ1 ), q :: τ2 → Maybe (T1 , τ2 ). (p 6= ⊥ ⇔ q 6= ⊥) ∧ (∀x :: τ1 . (p x, q (f x)) ∈ liftMaybe (lift(,) (id, f ))) ⇒ ∀y :: τ1 . cons p y = cons q (f y ) Zur Erinnerung, wir wollen beweisen: cons match (prod (:) [ ]) = cons id (prod (λa as → Just (a, as)) Nothing) 10 – 46/48 Wo beginnen? Das freie Theorem für cons :: ∀β. (β → Maybe (T1 , β)) → β → T2 , spezialisiert auf die Ebene von Funktionen, ist: ∀τ1 , τ2 , f :: τ1 → τ2 , f strict and total. ∀p :: τ1 → Maybe (T1 , τ1 ), q :: τ2 → Maybe (T1 , τ2 ). (p 6= ⊥ ⇔ q 6= ⊥) ∧ (∀x :: τ1 . (p x, q (f x)) ∈ liftMaybe (lift(,) (id, f ))) ⇒ ∀y :: τ1 . cons p y = cons q (f y ) Zur Erinnerung, wir wollen beweisen: cons match (prod (:) [ ]) = cons id (prod (λa as → Just (a, as)) Nothing) 10 – 47/48 Wo beginnen? Das freie Theorem für cons :: ∀β. (β → Maybe (T1 , β)) → β → T2 , spezialisiert auf die Ebene von Funktionen, ist: ∀τ1 , τ2 , f :: τ1 → τ2 , f strict and total. ∀p :: τ1 → Maybe (T1 , τ1 ), q :: τ2 → Maybe (T1 , τ2 ). (p 6= ⊥ ⇔ q 6= ⊥) ∧ (∀x :: τ1 . (p x, q (f x)) ∈ liftMaybe (lift(,) (id, f ))) ⇒ ∀y :: τ1 . cons p y = cons q (f y ) Zur Erinnerung, wir wollen beweisen: cons match (prod (:) [ ]) = cons id (prod (λa as → Just (a, as)) Nothing) 10 – 48/48 Wie weiter? Alles, was wir brauchen, ist eine Funktion f so dass: 1. f ist strict und total 2. ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) 3. f (prod (:) [ ]) = prod (λa as → Just (a, as)) Nothing (Hinweis: match 6= ⊥ ⇔ id 6= ⊥ ist trivial erfüllt.) 11 – 49/55 Wie weiter? Alles, was wir brauchen, ist eine Funktion f so dass: 1. f ist strict und total 2. ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) 3. f (prod (:) [ ]) = prod (λa as → Just (a, as)) Nothing 11 – 50/55 Wie weiter? Alles, was wir brauchen, ist eine Funktion f so dass: 1. f ist strict und total 2. ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) 3. f (prod (:) [ ]) = prod (λa as → Just (a, as)) Nothing Das freie Theorem für prod :: ∀β. (T1 → β → β) → β → β ist: ∀τ1 , τ2 , R ⊆ τ1 × τ2 , R strict, continuous, and bottom-reflecting. ∀p :: T1 → τ1 → τ1 , q :: T1 → τ2 → τ2 . (p 6= ⊥ ⇔ q 6= ⊥) ∧ (∀x :: T1 . (p x 6= ⊥ ⇔ q x 6= ⊥) ∧ ∀(y , z) ∈ R. (p x y , q x z) ∈ R) ⇒ ∀(v , w ) ∈ R. (prod p v , prod q w ) ∈ R 11 – 51/55 Wie weiter? Alles, was wir brauchen, ist eine Funktion f so dass: 1. f ist strict und total 2. ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) 3. f (prod (:) [ ]) = prod (λa as → Just (a, as)) Nothing Das freie Theorem für prod :: ∀β. (T1 → β → β) → β → β , spezialisiert auf die Ebene von Funktionen, ist: ∀τ1 , τ2 , f :: τ1 → τ2 , f strict and total. ∀p :: T1 → τ1 → τ1 , q :: T1 → τ2 → τ2 . (p 6= ⊥ ⇔ q 6= ⊥) ∧ (∀x :: T1 . (p x 6= ⊥ ⇔ q x 6= ⊥) ∧ ∀y :: τ1 . f (p x y ) = q x (f y )) ⇒ ∀z :: τ1 . f (prod p z) = prod q (f z) 11 – 52/55 Wie weiter? Alles, was wir brauchen, ist eine Funktion f so dass: 1. f ist strict und total 2. ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) 3. f (prod (:) [ ]) = prod (λa as → Just (a, as)) Nothing Das freie Theorem für prod :: ∀β. (T1 → β → β) → β → β , spezialisiert auf die Ebene von Funktionen, ist: ∀τ1 , τ2 , f :: τ1 → τ2 , f strict and total. ∀p :: T1 → τ1 → τ1 , q :: T1 → τ2 → τ2 . (p 6= ⊥ ⇔ q 6= ⊥) ∧ (∀x :: T1 . (p x 6= ⊥ ⇔ q x 6= ⊥) ∧ ∀y :: τ1 . f (p x y ) = q x (f y )) ⇒ ∀z :: τ1 . f (prod p z) = prod q (f z) 11 – 53/55 Wie weiter? Alles, was wir brauchen, ist eine Funktion f so dass: 1. f ist strict und total 2. ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) 3. f (prod (:) [ ]) = prod (λa as → Just (a, as)) Nothing Das freie Theorem für prod :: ∀β. (T1 → β → β) → β → β , spezialisiert auf die Ebene von Funktionen, ist: ∀τ1 , τ2 , f :: τ1 → τ2 , f strict and total. ∀p :: T1 → τ1 → τ1 , q :: T1 → τ2 → τ2 . (p 6= ⊥ ⇔ q 6= ⊥) ∧ (∀x :: T1 . (p x 6= ⊥ ⇔ q x 6= ⊥) ∧ ∀y :: τ1 . f (p x y ) = q x (f y )) ⇒ ∀z :: τ1 . f (prod p z) = prod q (f z) 11 – 54/55 Wie weiter? Alles, was wir brauchen, ist eine Funktion f so dass: 1. f ist strict und total 2. ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) 3. f (prod (:) [ ]) = prod (λa as → Just (a, as)) Nothing Das freie Theorem für prod :: ∀β. (T1 → β → β) → β → β , spezialisiert auf die Ebene von Funktionen, ist: ∀τ1 , τ2 , f :: τ1 → τ2 , f strict and total. ∀p :: T1 → τ1 → τ1 , q :: T1 → τ2 → τ2 . (p 6= ⊥ ⇔ q 6= ⊥) ∧ (∀x :: T1 . (p x 6= ⊥ ⇔ q x 6= ⊥) ∧ ∀y :: τ1 . f (p x y ) = q x (f y )) ⇒ ∀z :: τ1 . f (prod p z) = prod q (f z) 11 – 55/55 Fast geschafft Alles, was wir brauchen, ist eine Funktion f so dass: 1. f ist strict und total 2. ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) 3. ∀x :: T1 , y :: [T1 ]. f ((:) x y ) = (λa as → Just (a, as)) x (f y ) 4. f [ ] = Nothing (Hinweis: die = 6 ⊥“-Bedingungen sind wieder trivial erfüllt.) ” 12 – 56/61 Fast geschafft Alles, was wir brauchen, ist eine Funktion f so dass: 1. f ist strict und total 2. ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) 3. ∀x :: T1 , y :: [T1 ]. f ((:) x y ) = (λa as → Just (a, as)) x (f y ) 4. f [ ] = Nothing 12 – 57/61 Fast geschafft Alles, was wir brauchen, ist eine Funktion f so dass: 1. f ist strict und total 2. ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) 3. ∀x :: T1 , y :: [T1 ]. f ((:) x y ) = (λa as → Just (a, as)) x (f y ) 4. f [ ] = Nothing Die letzten beiden Bedingungen lassen keine andere Wahl als: f [] = Nothing f (x : y ) = Just (x, f y ) 12 – 58/61 Fast geschafft Alles, was wir brauchen, ist: 1. f ist strict und total 2. ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) Die letzten beiden Bedingungen lassen keine andere Wahl als: f [] = Nothing f (x : y ) = Just (x, f y ) 12 – 59/61 Fast geschafft Alles, was wir brauchen, ist: 1. f ist strict und total 2. ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) f [] = Nothing f (x : y ) = Just (x, f y ) Dieses f ist strict und total! 12 – 60/61 Fast geschafft 2. ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) ? f [] = Nothing f (x : y ) = Just (x, f y ) 12 – 61/61 Schließlich . . . Wir haben: liftMaybe (lift(,) (id, f )) = {(⊥, ⊥), (Nothing, Nothing)} ∪ {(Just x1 , Just y1 ) | (x1 , y1 ) ∈ lift(,) (id, f )} lift(,) (id, f ) = {(⊥, ⊥)} ∪ {((x1 , x2 ), (y1 , y2 )) | x1 = y1 ∧ f x2 = y2 } 13 – 62/69 Schließlich . . . Wir haben: liftMaybe (lift(,) (id, f )) = {(⊥, ⊥), (Nothing, Nothing)} ∪ {(Just x1 , Just y1 ) | (x1 , y1 ) ∈ lift(,) (id, f )} lift(,) (id, f ) = {(⊥, ⊥)} ∪ {((x1 , x2 ), (y1 , y2 )) | x1 = y1 ∧ f x2 = y2 } Um zu zeigen, dass ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) , 13 – 63/69 Schließlich . . . Wir haben: liftMaybe (lift(,) (id, f )) = {(⊥, ⊥), (Nothing, Nothing)} ∪ {(Just x1 , Just y1 ) | (x1 , y1 ) ∈ lift(,) (id, f )} lift(,) (id, f ) = {(⊥, ⊥)} ∪ {((x1 , x2 ), (y1 , y2 )) | x1 = y1 ∧ f x2 = y2 } Um zu zeigen, dass ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) , untersuchen wir alle Fälle für Eingaben von match und f , unter Verwendung der Definitionen: match [ ] = Nothing match (a : as) = Just (a, as) f [] = Nothing f (x : y ) = Just (x, f y ) 13 – 64/69 Schließlich . . . Wir haben: liftMaybe (lift(,) (id, f )) = {(⊥, ⊥), (Nothing, Nothing)} ∪ {(Just x1 , Just y1 ) | (x1 , y1 ) ∈ lift(,) (id, f )} lift(,) (id, f ) = {(⊥, ⊥)} ∪ {((x1 , x2 ), (y1 , y2 )) | x1 = y1 ∧ f x2 = y2 } Um zu zeigen, dass ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) , untersuchen wir alle Fälle für Eingaben von match und f , unter Verwendung der Definitionen: match [ ] = Nothing match (a : as) = Just (a, as) f [] = Nothing f (x : y ) = Just (x, f y ) 13 – 65/69 Schließlich . . . Wir haben: liftMaybe (lift(,) (id, f )) = {(⊥, ⊥), (Nothing, Nothing)} ∪ {(Just x1 , Just y1 ) | (x1 , y1 ) ∈ lift(,) (id, f )} lift(,) (id, f ) = {(⊥, ⊥)} ∪ {((x1 , x2 ), (y1 , y2 )) | x1 = y1 ∧ f x2 = y2 } Um zu zeigen, dass ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) , untersuchen wir alle Fälle für Eingaben von match und f , unter Verwendung der Definitionen: match [ ] = Nothing match (a : as) = Just (a, as) f [] = Nothing f (x : y ) = Just (x, f y ) 13 – 66/69 Schließlich . . . Wir haben: liftMaybe (lift(,) (id, f )) = {(⊥, ⊥), (Nothing, Nothing)} ∪ {(Just x1 , Just y1 ) | (x1 , y1 ) ∈ lift(,) (id, f )} lift(,) (id, f ) = {(⊥, ⊥)} ∪ {((x1 , x2 ), (y1 , y2 )) | x1 = y1 ∧ f x2 = y2 } Um zu zeigen, dass ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) , untersuchen wir alle Fälle für Eingaben von match und f , unter Verwendung der Definitionen: match [ ] = Nothing match (a : as) = Just (a, as) f [] = Nothing f (x : y ) = Just (x, f y ) 13 – 67/69 Schließlich . . . Wir haben: liftMaybe (lift(,) (id, f )) = {(⊥, ⊥), (Nothing, Nothing)} ∪ {(Just x1 , Just y1 ) | (x1 , y1 ) ∈ lift(,) (id, f )} lift(,) (id, f ) = {(⊥, ⊥)} ∪ {((x1 , x2 ), (y1 , y2 )) | x1 = y1 ∧ f x2 = y2 } Um zu zeigen, dass ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) , untersuchen wir alle Fälle für Eingaben von match und f , unter Verwendung der Definitionen: match [ ] = Nothing match (a : as) = Just (a, as) f [] = Nothing f (x : y ) = Just (x, f y ) 13 – 68/69 Schließlich . . . Wir haben: liftMaybe (lift(,) (id, f )) = {(⊥, ⊥), (Nothing, Nothing)} ∪ {(Just x1 , Just y1 ) | (x1 , y1 ) ∈ lift(,) (id, f )} lift(,) (id, f ) = {(⊥, ⊥)} ∪ {((x1 , x2 ), (y1 , y2 )) | x1 = y1 ∧ f x2 = y2 } Um zu zeigen, dass ∀x :: [T1 ]. (match x, id (f x)) ∈ liftMaybe (lift(,) (id, f )) , untersuchen wir alle Fälle für Eingaben von match und f , unter Verwendung der Definitionen: match [ ] = Nothing match (a : as) = Just (a, as) f [] = Nothing f (x : y ) = Just (x, f y ) Fertig! 13 – 69/69 Fazit Typangaben: ◮ schränken das Verhalten von Programmen ein ◮ erlauben daher die Herleitung von Aussagen über Programme 14 – 70/75 Fazit Typangaben: ◮ schränken das Verhalten von Programmen ein ◮ erlauben daher die Herleitung von Aussagen über Programme Erhaltene Programmtransformationen: ◮ bauen insbesondere auf Polymorphismus und Higher-Order“ ” 14 – 71/75 Fazit Typangaben: ◮ schränken das Verhalten von Programmen ein ◮ erlauben daher die Herleitung von Aussagen über Programme Erhaltene Programmtransformationen: ◮ ◮ bauen insbesondere auf Polymorphismus und Higher-Order“ ” können nicht-triviale Nebenbedingungen haben 14 – 72/75 Fazit Typangaben: ◮ schränken das Verhalten von Programmen ein ◮ erlauben daher die Herleitung von Aussagen über Programme Erhaltene Programmtransformationen: ◮ ◮ bauen insbesondere auf Polymorphismus und Higher-Order“ ” können nicht-triviale Nebenbedingungen haben Korrektheitsbeweise: ◮ sind zum Teil automatisierbar ◮ wo nicht, zumindest systematisch/zielgetrieben 14 – 73/75 Fazit Typangaben: ◮ schränken das Verhalten von Programmen ein ◮ erlauben daher die Herleitung von Aussagen über Programme Erhaltene Programmtransformationen: ◮ ◮ bauen insbesondere auf Polymorphismus und Higher-Order“ ” können nicht-triviale Nebenbedingungen haben Korrektheitsbeweise: ◮ sind zum Teil automatisierbar ◮ wo nicht, zumindest systematisch/zielgetrieben (ähnlich für andere Transformationen [V., FLOPS’08]) 14 – 74/75 Fazit Typangaben: ◮ schränken das Verhalten von Programmen ein ◮ erlauben daher die Herleitung von Aussagen über Programme Erhaltene Programmtransformationen: ◮ ◮ bauen insbesondere auf Polymorphismus und Higher-Order“ ” können nicht-triviale Nebenbedingungen haben Korrektheitsbeweise: ◮ sind zum Teil automatisierbar ◮ wo nicht, zumindest systematisch/zielgetrieben (ähnlich für andere Transformationen [V., FLOPS’08]) Weiter von Interesse: ◮ größere Abdeckung von Sprachkonstrukten ◮ noch ausdrucksstärkere Typsysteme 14 – 75/75 Literatur I A. Gill, J. Launchbury, and S.L. Peyton Jones. A short cut to deforestation. In Functional Programming Languages and Computer Architecture, Proceedings, pages 223–232. ACM Press, 1993. P. Johann and J. Voigtländer. Free theorems in the presence of seq. In Principles of Programming Languages, Proceedings, pages 99–110. ACM Press, 2004. P. Johann and J. Voigtländer. A family of syntactic logical relations for the semantics of Haskell-like languages. Information and Computation, 207(2):341–368, 2009. 15 – 76/78 Literatur II S.L. Peyton Jones, A. Tolmach, and C.A.R. Hoare. Playing by the rules: Rewriting as a practical optimisation technique in GHC. In Haskell Workshop, Proceedings, pages 203–233, 2001. F. Stenger and J. Voigtländer. Parametricity for Haskell with imprecise error semantics. In Typed Lambda Calculi and Applications, Proceedings, volume 5608 of LNCS, pages 294–308. Springer-Verlag, 2009. J. Svenningsson. Shortcut fusion for accumulating parameters & zip-like functions. In International Conference on Functional Programming, Proceedings, pages 124–132. ACM Press, 2002. 16 – 77/78 Literatur III J. Voigtländer. Proving correctness via free theorems: The case of the destroy/build-rule. In Partial Evaluation and Program Manipulation, Proceedings, pages 13–20. ACM Press, 2008. J. Voigtländer. Semantics and pragmatics of new shortcut fusion rules. In Functional and Logic Programming, Proceedings, volume 4989 of LNCS, pages 163–179. Springer-Verlag, 2008. P. Wadler. Theorems for free! In Functional Programming Languages and Computer Architecture, Proceedings, pages 347–359. ACM Press, 1989. 17 – 78/78