Errors in Haskell • let average xs = div (sum xs) (length xs) in average [ ] 1 Errors in Haskell • let average xs = div (sum xs) (length xs) in average [ ] • let tail (x : xs) = xs in tail [ ] 1 Errors in Haskell • let average xs = div (sum xs) (length xs) in average [ ] • let tail (x : xs) = xs in tail [ ] • if · · · then error “some string” else · · · 1 Errors in Haskell • let average xs = div (sum xs) (length xs) in average [ ] • let tail (x : xs) = xs in tail [ ] • if · · · then error “some string” else · · · • let loop = loop in loop 1 Errors in Haskell • let average xs = div (sum xs) (length xs) in average [ ] • let tail (x : xs) = xs in tail [ ] • if · · · then error “some string” else · · · • let loop = loop in loop Traditionell, oft alle Fehlerursachen unter „⊥“ subsumiert. 1 Errors in Haskell • let average xs = div (sum xs) (length xs) in average [ ] • let tail (x : xs) = xs in tail [ ] • if · · · then error “some string” else · · · • let loop = loop in loop Traditionell, oft alle Fehlerursachen unter „⊥“ subsumiert. Besser, feinere Unterscheidung. Etwa wie folgt: Ok v : nicht fehlerbehaftet Bad “· · · ” : endlich fehlerbehaftet ⊥ : nicht terminierend 1 Fortpflanzung von Fehlern • tail [1/0, 2.5] Ok ((Ok 2.5) : (Ok [ ])) 2 Fortpflanzung von Fehlern • tail [1/0, 2.5] Ok ((Ok 2.5) : (Ok [ ])) • (λx → 3) (error “· · · ”) Ok 3 2 Fortpflanzung von Fehlern • tail [1/0, 2.5] Ok ((Ok 2.5) : (Ok [ ])) • (λx → 3) (error “· · · ”) • (error s) (· · · ) Ok 3 Bad s 2 Fortpflanzung von Fehlern • tail [1/0, 2.5] Ok ((Ok 2.5) : (Ok [ ])) • (λx → 3) (error “· · · ”) • (error s) (· · · ) Ok 3 Bad s • case (error s) of {· · · } Bad s 2 Fortpflanzung von Fehlern • tail [1/0, 2.5] Ok ((Ok 2.5) : (Ok [ ])) • (λx → 3) (error “· · · ”) • (error s) (· · · ) Ok 3 Bad s • case (error s) of {· · · } Bad s • (error s1 ) + (error s2 ) ??? 2 Fortpflanzung von Fehlern • tail [1/0, 2.5] Ok ((Ok 2.5) : (Ok [ ])) • (λx → 3) (error “· · · ”) • (error s) (· · · ) Ok 3 Bad s • case (error s) of {· · · } Bad s • (error s1 ) + (error s2 ) ??? Abhängigkeit von Auswertungsreihenfolge führt zu erheblichen Einschränkungen der Implementationsfreiheit! 2 Imprecise Error Semantics [Peyton Jones et al. 1999] Grundidee: Ok v : nicht fehlerbehaftet Bad {· · · } : endlich fehlerbehaftet, nichtdeterministisch ⊥ : nicht terminierend 3 Imprecise Error Semantics [Peyton Jones et al. 1999] Grundidee: Ok v : nicht fehlerbehaftet Bad {· · · } : endlich fehlerbehaftet, nichtdeterministisch ⊥ : nicht terminierend Definiertheits-Ordnung: Bad Ok ⊥ 3 Imprecise Error Semantics [Peyton Jones et al. 1999] Grundidee: Ok v : nicht fehlerbehaftet Bad {· · · } : endlich fehlerbehaftet, nichtdeterministisch ⊥ : nicht terminierend Definiertheits-Ordnung: Bad e2 Bad e1 e2 ⊆ e1 Bad Ok ⊥ 3 Imprecise Error Semantics [Peyton Jones et al. 1999] Fortpflanzung von Fehlern: • (error s1 ) + (error s2 ) Bad {s1 , s2 } 4 Imprecise Error Semantics [Peyton Jones et al. 1999] Fortpflanzung von Fehlern: • (error s1 ) + (error s2 ) • 3 + (error s) Bad {s1 , s2 } Bad {s} 4 Imprecise Error Semantics [Peyton Jones et al. 1999] Fortpflanzung von Fehlern: • (error s1 ) + (error s2 ) • 3 + (error s) • loop + (error s) Bad {s1 , s2 } Bad {s} ⊥ 4 Imprecise Error Semantics [Peyton Jones et al. 1999] Fortpflanzung von Fehlern: • (error s1 ) + (error s2 ) • 3 + (error s) Bad {s1 , s2 } Bad {s} • loop + (error s) • (error s1 ) (error s2 ) ⊥ Bad {s1 , s2 } 4 Imprecise Error Semantics [Peyton Jones et al. 1999] Fortpflanzung von Fehlern: • (error s1 ) + (error s2 ) • 3 + (error s) Bad {s1 , s2 } Bad {s} • loop + (error s) • (error s1 ) (error s2 ) • (λx → 3) (error s) ⊥ Bad {s1 , s2 } Ok 3 4 Imprecise Error Semantics [Peyton Jones et al. 1999] Fortpflanzung von Fehlern: • (error s1 ) + (error s2 ) • 3 + (error s) Bad {s1 , s2 } Bad {s} • loop + (error s) • (error s1 ) (error s2 ) • (λx → 3) (error s) ⊥ Bad {s1 , s2 } Ok 3 • case (error s1 ) of {(x, y ) → error s2 } Bad {s1 , s2 } 4 Auswirkungen auf Programmäquivalenz „Normalerweise“: takeWhile p (map h l ) = map h (takeWhile (p ◦ h) l ) wobei: takeWhile :: (α → Bool) → [α] → [α] takeWhile p [ ] = [] takeWhile p (a : as) | p a = a : takeWhile p as | otherwise = [ ] map :: (α → β) → [α] → [β] map h [ ] = [] map h (a : as) = h a : map h as 5 Auswirkungen auf Programmäquivalenz „Normalerweise“: takeWhile p (map h l ) = map h (takeWhile (p ◦ h) l ) wobei: takeWhile :: (α → Bool) → [α] → [α] takeWhile p [ ] = [] takeWhile p (a : as) | p a = a : takeWhile p as | otherwise = [ ] map :: (α → β) → [α] → [β] map h [ ] = [] map h (a : as) = h a : map h as Aber nun: takeWhile null (map tail (error s)) 6= map tail (takeWhile (null ◦ tail) (error s)) 5 Auswirkungen auf Programmäquivalenz „Normalerweise“: takeWhile p (map h l ) = map h (takeWhile (p ◦ h) l ) wobei: takeWhile :: (α → Bool) → [α] → [α] takeWhile p [ ] = [] takeWhile p (a : as) | p a = a : takeWhile p as | otherwise = [ ] map :: (α → β) → [α] → [β] map h [ ] = [] map h (a : as) = h a : map h as Aber nun: takeWhile null (map tail (error s)) 6= map tail (takeWhile (null ◦ tail) (error s)) s s oder “empty list” 5 Auswirkungen auf Programmäquivalenz Denn: takeWhile (null ◦ tail) (error s) Bad {s, “empty list”} wobei: takeWhile p [ ] = [] takeWhile p (a : as) | p a = a : (takeWhile p as) | otherwise = [ ] tail [ ] = error “empty list” tail (a : as) = as null [ ] = True null (a : as) = False 6 Auswirkungen auf Programmäquivalenz Denn: takeWhile (null ◦ tail) (error s) Bad {s, “empty list”} wobei: takeWhile p [ ] = [] takeWhile p (a : as) | p a = a : (takeWhile p as) | otherwise = [ ] tail [ ] = error “empty list” tail (a : as) = as null [ ] = True null (a : as) = False 6 Auswirkungen auf Programmäquivalenz Denn: takeWhile (null ◦ tail) (error s) Bad {s, “empty list”} wobei: takeWhile p [ ] = [] takeWhile p (a : as) | p a = a : (takeWhile p as) | otherwise = [ ] tail [ ] = error “empty list” tail (a : as) = as null [ ] = True null (a : as) = False 6 Auswirkungen auf Programmäquivalenz Denn: takeWhile (null ◦ tail) (error s) Bad {s, “empty list”} wobei: takeWhile p [ ] = [] takeWhile p (a : as) | p a = a : (takeWhile p as) | otherwise = [ ] tail [ ] = error “empty list” tail (a : as) = as null [ ] = True null (a : as) = False 6 Auswirkungen auf Programmäquivalenz Denn: takeWhile (null ◦ tail) (error s) Bad {s, “empty list”} wobei: takeWhile p [ ] = [] takeWhile p (a : as) | p a = a : (takeWhile p as) | otherwise = [ ] tail [ ] = error “empty list” tail (a : as) = as null [ ] = True null (a : as) = False 6 Auswirkungen auf Programmäquivalenz Denn: takeWhile (null ◦ tail) (error s) Bad {s, “empty list”} wobei: takeWhile p [ ] = [] takeWhile p (a : as) | p a = a : (takeWhile p as) | otherwise = [ ] tail [ ] = error “empty list” tail (a : as) = as null [ ] = True null (a : as) = False 6 Auswirkungen auf Programmäquivalenz Denn: takeWhile (null ◦ tail) (error s) Bad {s, “empty list”} wobei: takeWhile p [ ] = [] takeWhile p (a : as) | p a = a : (takeWhile p as) | otherwise = [ ] tail [ ] = error “empty list” tail (a : as) = as null [ ] = True null (a : as) = False 6 Auswirkungen auf Programmäquivalenz Denn: takeWhile (null ◦ tail) (error s) während: Bad {s, “empty list”} takeWhile null (map tail (error s)) wobei: Bad {s} takeWhile p [ ] = [] takeWhile p (a : as) | p a = a : (takeWhile p as) | otherwise = [ ] map h [ ] = [] map h (a : as) = (h a) : (map h as) null [ ] = True null (a : as) = False 6 Auswirkungen auf Programmäquivalenz Denn: takeWhile (null ◦ tail) (error s) während: Bad {s, “empty list”} takeWhile null (map tail (error s)) wobei: Bad {s} takeWhile p [ ] = [] takeWhile p (a : as) | p a = a : (takeWhile p as) | otherwise = [ ] map h [ ] = [] map h (a : as) = (h a) : (map h as) null [ ] = True null (a : as) = False 6 Auswirkungen auf Programmäquivalenz Denn: takeWhile (null ◦ tail) (error s) während: Bad {s, “empty list”} takeWhile null (map tail (error s)) wobei: Bad {s} takeWhile p [ ] = [] takeWhile p (a : as) | p a = a : (takeWhile p as) | otherwise = [ ] map h [ ] = [] map h (a : as) = (h a) : (map h as) null [ ] = True null (a : as) = False 6 Auswirkungen auf Programmäquivalenz Denn: takeWhile (null ◦ tail) (error s) während: Bad {s, “empty list”} takeWhile null (map tail (error s)) wobei: Bad {s} takeWhile p [ ] = [] takeWhile p (a : as) | p a = a : (takeWhile p as) | otherwise = [ ] map h [ ] = [] map h (a : as) = (h a) : (map h as) null [ ] = True null (a : as) = False 6 Auswirkungen auf Programmäquivalenz Denn: takeWhile (null ◦ tail) (error s) während: Bad {s, “empty list”} takeWhile null (map tail (error s)) wobei: Bad {s} takeWhile p [ ] = [] takeWhile p (a : as) | p a = a : (takeWhile p as) | otherwise = [ ] map h [ ] = [] map h (a : as) = (h a) : (map h as) null [ ] = True null (a : as) = False 6 Auswirkungen auf Programmäquivalenz Denn: takeWhile (null ◦ tail) (error s) während: Bad {s, “empty list”} takeWhile null (map tail (error s)) wobei: Bad {s} takeWhile p [ ] = [] takeWhile p (a : as) | p a = a : (takeWhile p as) | otherwise = [ ] map h [ ] = [] map h (a : as) = (h a) : (map h as) null [ ] = True null (a : as) = False 6 Auswirkungen auf Programmäquivalenz Denn: takeWhile (null ◦ tail) (error s) während: Bad {s, “empty list”} takeWhile null (map tail (error s)) Bad {s} Also: takeWhile null (map tail (error s)) 6= map tail (takeWhile (null ◦ tail) (error s)) 6 Auswirkungen auf Programmäquivalenz Denn: takeWhile (null ◦ tail) (error s) während: Bad {s, “empty list”} takeWhile null (map tail (error s)) Bad {s} Also: takeWhile null (map tail (error s)) 6= map tail (takeWhile (null ◦ tail) (error s)) Man stelle sich dies in folgendem Kontext vor: catchJust errorCalls (evaluate · · ·) (λs → if s == “empty list” then return [[42]] else return [ ]) 6 Freies Theorem Bisheriger Kenntnisstand: g p (map h l ) = map h (g (p ◦ h) l ) für jedes g :: (α → Bool) → [α] → [α], wenn • p 6= ⊥, • h strikt (h ⊥ = ⊥) und • h total (∀x 6= ⊥. h x 6= ⊥). 7 Freies Theorem Bisheriger Kenntnisstand: g p (map h l ) = map h (g (p ◦ h) l ) für jedes g :: (α → Bool) → [α] → [α], wenn • p 6= ⊥, • h strikt (h ⊥ = ⊥) und • h total (∀x 6= ⊥. h x 6= ⊥). Was sind entsprechende Bedingungen „in echt“? Bad Ok ⊥ 7 Schweiß und Tränen . . . . . . durchgängige Formalisierung der Semantik 8 Schweiß und Tränen . . . . . . durchgängige Formalisierung der Semantik . . . Einstieg in Beweis des Parametrizitäts-Theorems 8 Schweiß und Tränen . . . . . . durchgängige Formalisierung der Semantik . . . Einstieg in Beweis des Parametrizitäts-Theorems . . . Betrachtung der interessanten Induktionsfälle 8 Schweiß und Tränen . . . . . . durchgängige Formalisierung der Semantik . . . Einstieg in Beweis des Parametrizitäts-Theorems . . . Betrachtung der interessanten Induktionsfälle . . . Identifizierung geeigneter Bedingungen auf Relationsebene 8 Schweiß und Tränen . . . . . . durchgängige Formalisierung der Semantik . . . Einstieg in Beweis des Parametrizitäts-Theorems . . . Betrachtung der interessanten Induktionsfälle . . . Identifizierung geeigneter Bedingungen auf Relationsebene . . . Anpassung relationaler Aktionen 8 Schweiß und Tränen . . . . . . durchgängige Formalisierung der Semantik . . . Einstieg in Beweis des Parametrizitäts-Theorems . . . Betrachtung der interessanten Induktionsfälle . . . Identifizierung geeigneter Bedingungen auf Relationsebene . . . Anpassung relationaler Aktionen . . . Vervollständigung allgemeiner Beweis 8 Schweiß und Tränen . . . . . . durchgängige Formalisierung der Semantik . . . Einstieg in Beweis des Parametrizitäts-Theorems . . . Betrachtung der interessanten Induktionsfälle . . . Identifizierung geeigneter Bedingungen auf Relationsebene . . . Anpassung relationaler Aktionen . . . Vervollständigung allgemeiner Beweis . . . Übertragung der Bedingungen auf Funktionsebene 8 Schweiß und Tränen . . . . . . durchgängige Formalisierung der Semantik . . . Einstieg in Beweis des Parametrizitäts-Theorems . . . Betrachtung der interessanten Induktionsfälle . . . Identifizierung geeigneter Bedingungen auf Relationsebene . . . Anpassung relationaler Aktionen . . . Vervollständigung allgemeiner Beweis . . . Übertragung der Bedingungen auf Funktionsebene . . . Anwendung auf konkrete Funktionen 8 . . . Anwendung auf „takeWhile“ Für jedes g :: (α → Bool) → [α] → [α], g p (map h l ) = map h (g (p ◦ h) l ) 9 . . . Anwendung auf „takeWhile“ Für jedes g :: (α → Bool) → [α] → [α], g p (map h l ) = map h (g (p ◦ h) l ) vorausgesetzt • p und h nicht fehlerbehaftet, h p Bad Ok ⊥ 9 . . . Anwendung auf „takeWhile“ Für jedes g :: (α → Bool) → [α] → [α], g p (map h l ) = map h (g (p ◦ h) l ) vorausgesetzt • p und h nicht fehlerbehaftet, • h ⊥ = ⊥, h p Bad Ok ⊥ h 9 . . . Anwendung auf „takeWhile“ Für jedes g :: (α → Bool) → [α] → [α], g p (map h l ) = map h (g (p ◦ h) l ) vorausgesetzt • p und h nicht fehlerbehaftet, • h ⊥ = ⊥, • h verhält sich als Identität auf endlichen Fehlern, und h h h p h Bad Ok ⊥ h 9 . . . Anwendung auf „takeWhile“ Für jedes g :: (α → Bool) → [α] → [α], g p (map h l ) = map h (g (p ◦ h) l ) vorausgesetzt • p und h nicht fehlerbehaftet, • h ⊥ = ⊥, • h verhält sich als Identität auf endlichen Fehlern, und • h bildet Nichtfehler auf Nichtfehler ab. h h h h p h Bad Ok ⊥ h 9