Data Specification (Beispiel 3) Beispiel 3: natürliche Zahlen Nat = data specification nat = 0 | . +1 (. −1 : nat); variables n : nat → nat; order predicates . < . : nat × nat; end data specification n +1 −1 = n; (: 0 −1 ist unspezifiziert :) ¬ n < n; m < n ∧ n < k → m < k ; ¬ n < 0; m < n +1 ↔ (m = n ∨ m < n) Generierte Axiome: 130 Data Specification (Beispiel 4) Beispiel 4: Listen List = data specification using Nat, Elem; list = . + . (. .first : elem; . .rest : list) with consp | [] with nilp; variables l : list; (: ist Unterterm für Listen = ist Endstück :) order predicates . ≪ . : list × list; size functions length : list → nat; end data specification 131 Selektoren als partielle Funktionen Problem: Was tun, wenn ein Selektor auf den falschen Summand angewandt wird? Hier: Was sind [] .first und [] .rest? .first und .rest sollten für [] gar nicht definiert sein ⇒ Partielle Funktionen In KIV: []. first und [].rest sind unspezifiziert (i. e. kein Axiom) Semantik: [].rest ist in jeder Algebra (Datenstruktur) irgendeine andere Liste ⇒ Echte Implementierung muss irgendeine Liste zurückgeben Pragmatik: Formeln mit [].rest sollten nicht vorkommen. Wenn doch, muss die Sequenz ohne die Formeln beweisbar sein. Bem.: Alternativen sind möglich aber komplizierter (u.a. Fehlerelemente, echte Partialität, etc.) 132 Data Specification (Beispiel 3) Neue Axiome für List3: nilp([]); ¬ nilp(a + l); ¬ consp([]); consp(a + l); (a + l).first = a; (: kein Axiom für [] .first :) (a + l).rest = l; (: kein Axiom für [] .rest :) ¬ l ≪ []; l ≪ (a + l 1 ) ↔ l = l 1 ∨ l ≪ l 1 ; (: beweisbar, aber der Einfachheit halber generiert :) ¬ l ≪ l ; l1 ≪ l2 ∧ l2 ≪ l3 → l1 ≪ l3 ; length([]) = 0; length(a + l) = length(l) +1; 133 Data Specification allgemein Definition (Datendefinition) Eine Datendefinition D hat die Form (optionales in eckigen Klammern): s = c1 (sel1,1 : s1,1 ; . . . ; sel1,n1 : s1,n1 ) [with p1 ] | ... | ck (selk,1 : sk,1 ; . . . ; selk,nk : sk,nk ) [with pk ]; [order predicate . ≪ .;][size function sz;] Überladen: seli,j = seli′ ,j ′ erlaubt, falls si,j = si′ ,j ′ , ansonsten alle Operationen paarweise verschieden. 134 Eigenschaften von Data Specifications Satz: Alle Data Specifications sind immer konsistent. Sie sind immer monomorph bis auf unspezifizierte Selektoren und Parameter. Intuition bei Listen ist also: 1. Wir haben exakt den Datentyp der Listen beschrieben. 2. Für die Realisierung auf einem Rechner ist lediglich offen, • was für ein Elementtyp verwendet wird und • was für eine Element bzw. Liste [].first und [].rest zurückgeben. 135 Datentyp-Definitionen in Java Implementierung von Datentyp-Definitionen in Java: abstract class s {} /* Dies entspricht der Sorte s */ /* Subklasse für Konstruktor c1 */ class c1 extends s { s11 sel11; /* ein Feld pro Selektor */ s12 sel12; ... s1n1 : sel1n1; /* Konstruktor */ public c1(s11 se1, s12 se2, ...) { sel11 = se1; sel12 = se2; ...} } /* Subklasse für Konstruktor c2 */ class c2 extends s { ... } 136 Strukturierte Spezifikation: Anreicherung um nichtrekursive und rekursive Definitionen 137 Strukturierte Spezifikationen Motivation Spezifikationen werden sehr schnell sehr groß ⇒ Strukturierung erforderlich 1. Übersichtlichkeit & Verständlichkeit 2. Wiederverwendung von Komponenten 3. Unabhängige Entwicklung von Komponenten 4. Strukturierung und Wiederverwendung von Beweisen 138 Strukturierte Spezifikationen in KIV • KIV zeigt strukt. Spezifikationen als Spezifikationsgraph an • In KIV enthält jeder Knoten neben der Spezifikation außerdem eine Theorembasis • Die Theorembasis enthält eine Menge von Theoremen (Sequenzen) über der Gesamtsignatur • Für jede Sequenz kann ein Beweis vorhanden sein, der zeigt, daß die Sequenz aus SP und anderen Theoremen folgt (keine zyklischen Abhängigkeiten) • Theoreme aus darunterliegenden Spezifikation werden mit insert spec-lemma statt insert lemma angewandt • KIV hat ein Korrektheitsmanagement, um Abhängigkeiten zwischen Beweisen zu verwalten 139 Strukturierte Spezifikationen: Vereinigung und Anreicherung • Vereinigung ⋆ Wirft Signaturen + Axiome zusammen ⋆ Syntax: union specification <SPEC1> + <SPEC2> + ... end union specification ⋆ Nichtdisjunkte Vereinigung nur, wenn gemeinsamer Teil in gleicher Unterspezifikation (sonst Gefahr der Inkonsistenz!) • Anreicherung ⋆ Addiert neue Signaturen + Axiome ⋆ Syntax: enrich <SPEC1>, <SPEC2>, ... with <signature> <induction> <axioms> end enrich ⋆ Implizit: Ausgangsspezifikation der Anreicherung ist die Vereinigung von <SPEC1>, <SPEC2>, ... 140 Anreicherung: Vorgehen • Neue Axiome hinzuzunehmen macht eine Spezifikation sehr schnell inkonsistent (siehe Versuch 3!) ⇒ Gewünscht: Nur „konsistenzerhaltende“ Anreicherungen • Formaler Begriff: Konsistenzerhaltend = Hierarchiepersistent • Neue Operationen sollten „monomorphieerhaltend“ sein: Wenn genau ein Datentyp spezifiziert, sollte die Operation ebenfalls eindeutig festgelegt sein • Formaler Begriff: Monomorphieerhaltend = Eindeutig Vorgehen: Anreicherung nur durch rekursive und nichtrekursive Definitionen für neue Operationen. Diese sind hierarchiepersistent und eindeutig. 141 Nichtrekursive Definitionen (Motivation) • Grundidee: Definition als Abkürzung einer grossen Formel • Prädikat: p(x) ↔ ϕ (p kommt in Formel ϕ nicht vor) • Funktion: f (x) = t (f kommt im Term t nicht vor) 142 Beispiel: Anreicherung um nichtrek. Prädikat NatDiv = enrich Nat with predicates | : nat × nat; axioms m | n ↔ ∃ k. k * m = n; end enrich Die Anreicherung NatDiv ist hierarchiepersistent und eindeutig. 143 Beispiel: Anr. um nichtrek. Prädikat (mit FU) NatPrime = enrich NatDiv with predicates prime : nat; axioms ¬ prime(0); ¬ prime(1); n ≥ 2 → (prime(n) ↔ ∀ m. m | n → m = 1 ∨ m = n); end enrich Die Anreicherung NatPrime ist hierarchiepersistent und eindeutig (FU nach n = 0, n = 1, n ≥ 2 ist vollständig). 144 Beispiel: Anreicherung um nichtrek. Fkt. Nat12max = enrich Nat with constants 1 : nat; 2 : nat; functions max : nat × nat → nat; axioms 1 = 0 +1; 2 = 0 +1 +1; m < n → max(m, n) = n; ¬ m < n → max(m, n) = m; end enrich Die Anreicherung Nat12max ist hierarchiepersistent und eindeutig (beachte: In KIV sind die Zahlen 1,2 schon vordefiniert). Beachte: Das Axiom max(m,n) = k ↔ (m < n ∧ k = n) ∨ (¬ m < n ∧ k = m) bildet keine nichtrek. Definition. Axiome f(x) = y ↔ . . . sollte man vermeiden! 145 Weitere Beispiele für nichtrekursive Definitionen m ≤ n ↔ ∃ k. m + k = n m < n ↔ m ≤ n ∧ m 6= n m>n↔n<m even(n) ↔ ∃ m. n = m + m odd(n) ↔ ¬ even(n) m < n → min(m, n) = m, ¬ m < n → min(m, n) = n x ≥ 0 → abs(x) = x, x < 0 → abs(x) = −x last(l + (a + [])) = a (: unvollst. Def :) ((a + l)).first = a (: unvollst. Def :) a ∈ l ↔ ∃ l′ , l′′ . l′ + a + l′′ = l nodups(l) ↔ ¬ ∃ l1 , l2 , l3 , a. l = l1 + a + l2 + a + l3 146 Nichtrekursive Definitionen allgemein • Prädikat: p(x) ↔ ϕ (p kommt in Formel ϕ nicht vor) • Funktion: f (x) = t (f kommt im Term t nicht vor) • Prädikat mit 2 vollständigen Fällen: ε → (q(x, y) ↔ ϕ1 ), ¬ ε → (q(x, y) ↔ ϕ2 ) (p kommt in ε, ϕ1 , ϕ2 nicht vor) • Funktionen mit 3 unvollständigen Fällen: ε ∧ δ → f (x, y) = t1 , ε ∧ ¬ δ → f (x, y) = t2 , ¬ ε ∧ δ → f (x, y) = t3 (f kommt in ε, δ , ϕ1 , ϕ2 nicht vor) Satz: Die Hinzunahme einer neuen Operation (Funktion oder Prädikat) mit nichtrekursiver Definition ist immer hierarchiepersistent. Wenn die Fälle vollständig sind, ist die Erweiterung sogar eindeutig. 147 Rekursive Definitionen (Motivation) • Datentypen bestehen aus (der Semantik von) Konstruktortermen • Rekursive Definitionen geben Definition durch Reduktion auf kleinere Konstruktorterme • Entsprechen einem rekursiven Programm, das den Term abläuft Length = enrich List with functions length : list → nat; axioms length([]) = 0; length(a + l) = length(l) +1; end enrich Satz: Die Hinzunahme einer neuen Operation mit rekursiver Definition zu einem freien Datentyp ist immer hierarchiepersistent. Wenn die Fälle vollständig sind, ist die Erweiterung immer eindeutig. 148 Beispiel: Anreicherung um rek. Definition Append = enrich List with functions . + . : list × list → list; axioms [] + l = l; (a + l) + l’ = a + (l + l’); end enrich Append ist hierarchiepersistent und eindeutig (+ ist überladen: Sowohl Element vor Liste hängen (rot und schwarz), als auch 2 Listen zusammenhängen (blau)). 149 Beispiel: Anreicherung um rek. Prädikat (FU) Sorted = enrich List with predicates sorted : list axioms ordered([]), ordered(a + []), ordered(a + b + l) ↔ a < b ∧ ordered(b + l) end enrich Ordered ist hierarchiepersistent und eindeutig. Das zweite und dritte Axiom sind äquivalent zu: l’= [] → ordered(a + l’), l’= b + l → ordered(a + l’) ↔ a < b ∧ ordered(l’) (vollständige Fallunterscheidung). Die Gleichungen vor der Implikation wurden eingesetzt (bessere Simplifierregel!). 150 Rekursive Definition allgemein hier speziell: Funktionen/Prädikate mit 3 Argumenten Definitionen im ersten rekursiv, Listen (allgemein: 1 Axiom pro Konstruktor) • Prädikat: p-base: p([], y, z) ↔ ϕ1 p-rec : p(a + x, y, z) ↔ ϕ2 (x, y, z verschieden. ϕ1 hat keine Aufrufe von p. ϕ2 hat nur Aufrufe von p der Form p(x, t1 , t2 )) • Funktion: f-base: f ([], x, y) = t0 f-rec: f (a + x, y, z) = t (x, y, z verschieden. t1 hat keine Aufrufe von f . t hat nur Aufrufe von f der Form f (x, t4 , t5 )) • Zusätzliche Fallunterscheidung in den Axiomen ist ok. Z.B. statt f (a + z, y, z) = t 3 unvollständigen Fälle: ε ∧ δ → f (a + x, y, z) = t1 , ε ∧ ¬ δ → f (a + x, y, z) = t2 , ¬ ε ∧ δ → f (a + x, y, z) = t3 (ε, δ , t1 , t2 , t3 enthalten nur Aufrufe von f der Form f (x, t4 , t5 ) 151 Strukturierte Spezifikationen: Umbenennung und Parameter 152 Strukturierte Spezifikationen: Umbenennung Umbenennung: • Benennt die Operationen einer Spezifikation um • Nützlich um 2 Kopien zu erhalten • Syntax: rename <SPEC> by morphism <renaming1>; ... <renamingn>; end rename • renaming = <sort/op/var> → <sort/op/var>; • Identische Umbenennungen weglassen (werden beim Ansehen der Spezifikation aber angezeigt) • Nicht 2 Symbole auf dasselbe abbilden: Injektiv umbenennen • Entweder alle Variablen oder keine umbenennen 153 Beispiel Umbenennung: Listen zu Stacks List by morphism list → stack; [] → empty; (: Typangabe für überladenes Symbol :) + :: (elem × list → list) → push; (: pop nicht mehr postfix, Schreibweise also pop(x), default ist in/prae/postfix uebernehmen :) .rest → pop prio 0; (: top soll nun praefix sein :) .first → top .; (: eigentlich keine Stack-Operation, nur um Overloading zu zeigen :) + :: (list × list → list) → concat prio 0; x → st; y → st0; z → st1; rename end rename 154 Strukturierte Spezifikationen: Generische Spezifikation Generische Spezifikation: • Syntax: generic specification parameter <SPEC> using <SPEC1>, ..., <SPECn> target <signature> <induction> <axioms> end generic specification • Wie Anreicherung (von <SPEC>, <SPEC1>, ..., <SPECn>), nur wird von <SPEC> explizit gesagt, dass es ein Parameter ist. • Vereinigung und Anreicherung übernimmt den (oder die) Parameter der Unterspezifikationen • Variante: generic data specification: Wie data specification nur mit Parameter 155 Strukturierte Spezifikationen: Aktualisierung (1) Aktualisierung: • Instanziert Parameter (oder einen Parameter) einer Spezifikation • Beispiel: Listen beliebiger Elemente → Listen von Zahlen • Syntax: actualize <SPEC> with <ASPEC1>,...,<ASPECn> by morphism <renaming1>; ... <renamingn>; end actualize • renaming = <sort/op/var> → <sort/op/var>; • Die Vereinigung von <ASPEC1>,...,<ASPECn> heisst aktuelle Spezifikation • Identische Umbenennungen weglassen • Entweder alle Variablen oder keine umbenennen 156 Strukturierte Spezifikationen: Aktualisierung (2) Aktualisierung: • Der Parameter muss in die aktuelle Spez. abgebildet werden • Abbildung darf nicht-injektiv sein: pair(elem, elem) → pair(nat, nat) • Der Nicht-Parameter-Teil darf nur injektiv und disjunkt zur aktuellen Spezifikation umbenannt werden, z. B. list → natlist • Die instanzierten Axiome des Parameters müssen Axiome in der aktuellen Spezifikation sein • Verallgemeinerung instantiated specification: ⋆ Axiome werden bewiesen ⋆ mapping statt morphism erlaubt es, eine Sorte auf ein Tupel von Sorten abzubilden 157 Aktualisierung: Beispiel (1) Order = specification sorts elem; constants d : elem; predicates ≪ : elem × elem; variables a, b, c : elem; axioms ¬ a ≪ a; a ≪ b ∧ b ≪ c → a ≪ c; ¬ a ≪ d; (: d ist minimal :) end specification List-Ord = generic data specification parameter Order using Nat list = [] | . + . (. .first : elem; . .rest : list); size functions length : list → nat; end generic data specification 158 Aktualisierung: Beispiel (2) NatList = List-Ord with Nat by morphism list → natlist; elem → nat; ≪ → <; d → 0; a → n; b → n0; c → n1; actualize end actualize Die instanzierten Axiome (u. a. ¬ n < 0) sind (modulo Umbenennung) in Nat vorhanden. Die Listenoperationen (+, .rest etc.) werden nicht umbenannt (sie bekommen nur die neuen Argumentsorten). 159 Strukturierte Spezifikation: Nichtfreie Datentypen 160 Nichtfreie Datentypen: Der typische Fehler Orderedlist = enrich List3 with predicates .<. : elem × elem ; ordered : list ; axioms ¬ a < a; a < b ∨ a = b ∨ b < a; a < b ∧ b < c → a < c; ordered([]); ordered(a + []); ordered(a + b + l) ↔ a < b ∧ ordered (b + l); end enrich NICHT ∀ l. ordered(l) addieren!!! Das wäre INKONSISTENT!!! Allgemein: Ein generierter Datentyp enthält immer alle Konstruktorterme. Man kann nicht nachträglich welche ausschliessen. Man kann nur einen nichtfreien Datentyp bilden, der Terme identifiziert 161 Spezifikation nichtfreier Datentypen • Spezifikationen nichtfreier Datentypen werden sehr leicht inkonsistent oder uneindeutig • Konstruiere nichtfreien Datentyp dadurch, daß alle Terme, die die gleichen Elemente repräsentieren sollen, in einer Klasse zusammengefaßt werden • Deshalb als erstes nach der Bestimmung der Konstruktoren: Definiere Gleichheit durch Extensionalitätsaxiom: x = y ↔ ϕ(x, y) • Dann: Die in ϕ benutzten Operationen werden rekursiv definiert • Damit: Monomorph: Höchstens ein Datentyp spezifiziert • Vorsicht: Rek. Def. kann inkonsistent sein! • Jetzt: Arrays, im Praktikum: Mengen (einfacher) Einzige Ausnahme, bei der das Prinzip nicht funktioniert: integers (für die KIV-Axiome wurde ein Einzelbeweis für Monomorphie geführt) 162 Beispiel: Arrayspezifikation, Teil 1 Array= specification using Nat; Elem; sorts array; functions mkar .[.] .[.] #. : : : : nat × elem array × nat × elem array × nat array → → → → array; array; elem; nat; induction array generated by mkar, []; variables d : elem; a, a’ : array; 163 Beispiel: Arrayspezifikation, Teil 2 Festlegungen: • Konstruktor mkar(n, d) bekommt Initialelement d (Alternative: Unspezifizierte Initialisierung) • Für m ≥ # a ist Selektion a[m] unspezifiziert • Für m ≥ # a ist Modifikation Identität: a[m, d] = a axioms a = a’ ↔ # a = # a’ ∧ ∀ n. n < # a → a[n] = a’[n]; # mkar(n,d) = n; # a[m , d] = # a; m < n → mkar(n,d)[m] = d; m < # a → a[m , d][m] = d; n 6= m → a[m , d][n] = a[n]; end specification 164 Inkonsistente rekursive Definition • Annahmen: ⋆ Mengen von nat. Zahlen definiert ⋆ Die Mengen sind von ∅ und insert generiert ⋆ Für Mengen gilt: insert(a,insert(a,∅)) = insert(a,∅) • Rekursive Definition: sum(∅) = 0, sum(insert(a, s)) = a + sum(s) • Inkonsistent, da aus insert(1,insert(1,∅))) = insert(1,∅) folgt: 2 = sum(insert(1,insert(1,∅))) = sum(insert(1,∅)) = 1 • Korrekte Definition: sum(∅) = 0, ¬ a ∈ s → sum(insert(a, s)) = a + sum(s) (im Fall a ∈ s ist insert(a, s) = s) • Operation sum muss mit der Definition der Gleichheit auf Mengen, i. e. dem Extensionalitätsaxiom verträglich sein! 165 Konsistenz nichtfreier Datentypen 2 Alternativen, um Konsistenz sicherzustellen: 1. Semantische Überlegung: Es gibt den spezifizierten Datentyp auf Rechnern, und er erfüllt die Axiome ⇒ Konsistenz 2. Beweise über dem freien Datentyp, den man erhält, wenn man • Extensionalität x = y ↔ ϕ(x,y) weglässt. • generated by durch freely generated by ersetzt. • Idee: Der freie Datentyp ist konsistent und monomorph. Den nichtfreien erhält man, indem man Konstruktorterme, die per Extensionalität „gleich“ sind, in Klassen zusammenwirft. • Dazu muss ϕ eine Äquivalenzrelation sein: ϕ(a, a), ϕ(a, b) → ϕ(b, a), ϕ(a, b) ∧ ϕ(b, c) → ϕ(a, c) • Für „gleiche“ Terme t1 , t2 muss Operation f dasselbe liefern: ϕ(t1 , t2 ) → f (t1 ) = f (t2 ) bzw. ϕ(t1 , t2 ) → ϕ(f (t1 ), f (t2 )) • Äquivalenzrel. + Verträglichkeit = Kongruenz • Formeln über dem freien Datentyp beweisbar ⇒ Konsistenz 166 Korrekte Spez. der geordneten Listen (1) Orderedlist = specification sorts elem; ordlist; constants [] : ordlist; functions .+. : elem × ordlist → ordlist ; min : ordlist → elem ; butmin : ordlist → ordlist ; predicates . < . : elem × elem ; variables a, b, c : elem; l, l ′ : ordlist; induction ordlist generated by [], +; 167 Korrekte Spez. der geordneten Listen (2) axioms ¬ a < a; a < b ∨ a = b ∨ b < a; a < b ∧ b < c → a < c; l = l′ ↔ l = [] ∧ l’ = [] ∨ l 6= [] ∧ l’ 6= [] ∧ min(l) = min(l′ ) ∧ butmin(l) = butmin(l′ ); min(a + []) = a; a < b → min(a + b + l) = min(a + l); ¬ a < b → min(a + b + l) = min(b + l); butmin(a + []) = []; a < b → butmin(a + b + l) = b + butmin(a + l); ¬ a < b → butmin(a + b + l) = a + butmin(b + l); end specification 168 Zusammenfassung: Datentyp-Spezifikation Vorgehen zur Spezifikation von Datentypen: 1. Bestimme Datenmengen ⇒ Sorten 2. Bestimme Konstruktoren 3. Falls frei ⇒ data specification 4. Falls nichtfrei ⇒ Extensionalitätsaxiom + (rek. oder nichtrek.) Definition der im Axiom verwendeten Funktionen (mit evtl. Kongruenzbeweis) 5. Für weitere Funktionen: rek. oder nichtrek. Definitionen (enrichment) 169