Logika i teoria typów Wykład 11 Typy rekurencyjne (i indukcyjne) 18 maja 2016 Recursive types (Curry style) Positive and Negative + Principal idea: Type µp. τ (p) is a solution of X = τ (X ). For example, one identifies Therefore α = µp. q p with q α. y : q, x : α xyy... y : α Dangerous trick: Let ι = µp. p p. Then every closed term has type ι (no more SN). Positivity + + + + + + + Monotoniczność: Jeśli p występuje w ϕ tylko pozytywnie, to p p ϕ(p) ϕ(p ). Positive recursive types Pos(p) = {p}; Neg(p) = ; Pos(τ σ) = Neg(τ) Pos(σ); Neg(τ σ) = Pos(τ) Neg(σ); Pos(µp τ) = Pos(τ) {p}; Neg(µp τ) = Neg(τ) {p} Type µp τ is only permitted when p Neg(τ). Strong normalization holds (even in PA). But add any negative fixpoint and SN is lost. Incomparable with F wrt typability: λx. xx untypable. (Needs a negative fixpoint τ = τ... ) 22K typable. (Take α = µp. q p. Then K : α α.) Open problem: Incomparable with F ω? Positive recursive types Typy rekurencyjne w sensie Churcha? Decidable inhabitation (reduction to simple types). Decidable type assignment (unification with infinite regular trees). Uwaga: ostrożnie z drzewami: typy µp. p q i µp. (p q) q rozwijają się w to samo drzewo. Oznaczenie µ = µp. σ(p). Potrzebujemy wprowadzania i eliminacji. To pierwsze to operator in : σ(µ) µ: Γ M : σ(µ) Γ in(m) : µ
Jaki eliminator jest odpowiedni? Jaki eliminator jest odpowiedni? Pierwszy pomysł: Oznaczenie µ = µp. τ (p). Mamy in : τ (µ) µ Wprowadzamy operator out : µ τ (µ) i redukcję out(in M) M. To jest umiarkowanie ciekawe. (Niewiele można zdefiniować.) Per Martin-Löf: Reguły wprowadzania dla spójnika logicznego (typu danych) są pierwotne. Reguła eliminacji i zasada beta-redukcji są wtórne. Reguły wprowadzania określają jakie są kanoniczne formy elementów danego typu. Na przykład, dla alternatywy czyli koproduktu mamy dwie postaci kanoniczne: Γ M : α Γ lt(m) : α β Γ M : β Γ rt(m) : α β Alternatywa/koprodukt Alternatywa/koprodukt Dwie postaci kanoniczne = dwie reguły wprowadzania: Γ M : α Γ lt(m) : α β Γ M : β Γ rt(m) : α β Reguła eliminacji podaje sposób użycia elementów kanonicznych. Ma po jednej przesłance niegłównej dla każdej postaci kanonicznej. Dla alternatywy są takie dwie. Γ M : α β Γ, u:α R : τ Γ, v :β Q : τ Γ case M of [u]r or [v]q : τ Γ M : α Γ lt(m) : α β Γ M : β Γ rt(m) : α β Γ M : α β Γ, u:α R : τ Γ, v :β Q : τ Γ case M of [u]r or [v]q : τ Każda przesłanka niegłówna to sposób użycia innej postaci kanonicznej. To determinuje też zasadę beta-redukcji: case lt (P α ) of [x α ]M ρ, [y β ]N ρ = M[x := P] : ρ case rt (Q β ) of [x α ]M ρ, [y β ]N ρ = N[y := Q] : ρ Alternatywa/koprodukt Γ M : α Γ lt(m) : α β Γ M : β Γ rt(m) : α β Γ M : α β Γ, u:α R : τ Γ, v :β Q : τ Γ case M of [u]r or [v]q : τ Każda przesłanka niegłówna to sposób użycia innej postaci kanonicznej. To determinuje też zasadę beta-redukcji: case lt (P α ) of [x α ]M ρ, [y β ]N ρ = M[x := P] : ρ case rt (Q β ) of [x α ]M ρ, [y β ]N ρ = N[y := Q] : ρ To są niewłaściwe eliminacje Dla koniunkcji dostaniemy coś takiego: Γ M : A B Γ, x : A, y : B N : C Γ let M be x, y in N : C Beta-redukcja: let P, Q be x, y in N N[x := P][y := Q] Ćwiczenie: jaka koniunkcja jest w Coqu? Studium przypadku: liczby naturalne Przykład: liczby naturalne int 1 int, czyli int = µp. 1 p, gdzie typ 1 ma jeden element nil : 1. Niech int = µp. 1 p oraz in : 1 int int. Zero to element in(lt(nil)). Liczba n + 1 to element in(rt(n)). Uwaga: włożenie in : 1 int int, to moralnie to samo, co para 0, s : int (int int).
Typ rekurencyjny à la Church int = µp. 1 p Niech int = µp. 1 p. Jaki eliminator jest odpowiedni dla liczb naturalnych? Reguła wprowadzania: Γ M : 1 int Γ in(m) : int Reguła wprowadzania Γ M : 1 int Γ in(m) : int Zero to element in(lt(nil)). Liczba n + 1 to element in(rt(n)). Funkcja następnika: succ = λn int. in(rt(n)) sugeruje taką (naiwną) eliminację: Γ, x : 1 int R : τ Γ Elim M with [x]r : τ Trochę inaczej to samo int = µp. 1 p Zamiast jednej reguły wprowadzania Γ M : 1 int Γ in(m) : int Naiwna eliminacja rozbita na przypadki: dwie reguły dla dwóch przypadków: Γ nil : 1 Γ P : τ Γ, v :int Q : τ Γ Elim M with P or [v]q : τ Γ in(lt(nil)) : int Γ in(rt(m)) : int Z tego wychodzi taka beta-redukcja: Albo trochę przyjaźniej: Γ 0 : int Γ s(m) : int Elim 0 with P or [v]q P Elim s(n) with P or [v]q Q[v := n] int = µp. 1 p Jaka eliminacja dla int = µp. 1 p? Γ P : τ Γ, v :int Q : τ Γ Elim M with P or [v]q : τ Do przesłanek reguły Γ P : τ Γ, v :int Q : τ Elim s(n) with P or [v]q Q[v := n] Γ Elim M with P or [v]q : τ Sposób użycia postaci kanonicznej jest zdeterminowany przez sposób użycia jej składowych (cokolwiek to znaczy). Liczba n jest składową swojego następnika s(n). Zatem sposób użycia s(n) powinien zależeć od sposobu uzycia n. Naiwna reguła odwołuje się do Q jako do gotowego sposobu użycia dowolnej liczby naturalnej. (A my dopiero definiujemy taki sposób użycia! trzeba dodać sposób eliminacji V : τ dla v : int: Γ P : τ Γ, v :int, V : τ Q : τ Γ Elim M with P or [v, V ]Q : τ Sposób użycia s(n) określamy znając sposób użycia V dla n: Elim s(n) with P or [v, V ]Q Q[v := n, V :=?] Jaka eliminacja dla int = µp. 1 p? Wariant: Trochę inaczej to samo Γ P : τ Γ, v :int, V : τ Q : τ Γ Elim M with P or [v, V ]Q : τ Sposób użycia s(n) określamy znając sposób użycia V dla n: Elim s(n) with P or [v, V ]Q Q[v := n, V :=?] Elim s(n)with P or [v, V ]Q Q[v := n, V := Elim n with P or [v, V ]Q] Zamiast dwóch zmiennych Γ P : τ Γ, v :int, V : τ Q : τ Γ Elim M with P or [v, V ]Q : τ może być jedna: Γ P : τ Γ, V : int τ Q : τ Γ Elim M with P or [V ]Q : τ
Tradycyjna składnia Rekursja dla liczb naturalnych Regułę Γ P : τ Γ, v :int, V : τ Q : τ Γ Elim M with P or [v, V ]Q : τ Γ P : τ Γ Q : int τ τ Γ R τ M P Q : τ można napisać tak: Γ P : τ Γ Q : int τ τ Γ R τ M P Q : τ R τ 0 P Q P R τ (s n) P Q Q n (R τ n P Q) Operator R τ nazywamy rekursorem. System T Gödla R τ 0 P Q P R τ (s n) P Q Q n (R τ n P Q) Typy: Typy proste zbudowane z jednej stałej typowej int. (Czasem dodaje się bool, produkty itp.) Termy: Jak w rachunku lambda z typami prostymi plus stałe: 0 : int s : int int R τ : int τ (int τ τ ) τ, dla dowolnego typu τ. Redukcja: Zwykła beta-redukcja (może być eta) oraz: R τ 0 P Q P R τ (s n) P Q Q n (R τ n P Q) Dodawanie: λx int y int. R int y x (λuv. sv) Funkcje f 0 = s, f k+1 (n) = f n+1 k (1) są definiowalne. Niech M = λfxy. R int x y (λuv. fv). Wtedy F k+1 = λx int.mf k (sx)1. Funkcja Ackermanna a(k, n) = f k (n) jest definiowalna: A = λzy. R int int z s (λuϕx. Mϕ(sx)1)y Przykład: Funkcja poprzednika pred = λn int. R int n 0 (λxy. x) Własności systemu T Metoda Taita Definition: Stable (computable,reducible... ) terms: System T ma własność silnej normalizacji (dowód metodą Taita ). Definiowalne są wszystkie funkcje dowodliwie rekurencyjne w PA. Własność SN dla systemu T jest niezależna od PA. [[int]] := SN; [[τ σ]] := {M N(N [[τ ]] MN [[σ]])}; Główny lemat: Termy stabilne mają własność SN. Każdy term jest stabilny. To jest trudne Wycieranie arytmetyki Uwaga 1: Metoda Taita (zwana też metodą obliczalności) nie formalizuje się w PA, bo posługuje się dowolnymi zbiorami termów. Tego się nie da zakodować liczbami. Uwaga 2: Dla systemu F trzeba użyć metody kandydatów Girarda. To jest uogólnienie metody Taita, gdzie używa się dowolnych rodzin zbiorów. Tego nie zakodujemy nawet w arytmetyce drugiego rzędu. Weźmy aksjomat indukcji Peana z 1889 roku: τ (0) y (int(y) τ (y) τ (sy)) x(int(x) τ (x)). Napiszmy go trochę inaczej: x (int(x) τ (0) y(int(y) τ (y) τ (sy)) τ (x)). I wytrzyjmy zależności: int τ (int τ τ ) τ. Wychodzi typ rekursora!
Prostsza wersja rekursji: iteracja Jak to można uogólnić? Γ P : τ Γ Q : τ τ Γ It τ M P Q : τ It τ 0P Q P It τ (sn)p Q Q(It τ np Q) Ciekawostka: Typ iteratora It τ : int τ (τ τ ) τ jest wytarciem schematu indukcji Peana w wersji z 1891 roku: x (int(x) τ (0) y(τ (y) τ (sy)) τ (x)). Reguła dla iteratora: Γ P : τ Γ Q : τ τ Γ It τ M P Q : τ przepisana z użyciem koproduktu: Redukcja: Γ D : 1 τ τ Γ Elim τ D M : τ Elim τ D (in N 1 int ) D(case N of [x 1 ]lt(x) or [y int ]rt(elim τ Dy)) Co tu się dzieje? Γ D : 1 τ τ Γ Elim τ D M : τ Elim τ D (in N 1 int ) D(case N of [x 1 ]lt(x) or [y int ]rt(elim τ DN)) Definiujemy przez indukcję funkcję F = Elim τ D : int τ. Dla danego N : 1 int stosujemy indukcję, aplikując funkcję F, ale tylko na prawym kanale i dostajemy obiekt typu 1 τ. Inaczej: podnosimy typ funkcji F : int τ do typu 1 int 1 τ. To jest coś takiego: Lift(F )(N) = case N of [x 1 ]lt(x) or [y int ]rt(fn) Uzyskujemy wynik typu 1 τ, do którego aplikujemy D. Przypadek ogólny Co tu się dzieje? Γ D : 1 τ τ Γ Elim τ D M : τ Elim τ D (in N 1 int ) D(case N of [x 1 ]lt(x) or [y int ]rt(elim τ DN)) Lift(F )(N) = case N of [x 1 ]lt(x) or [y int ]rt(fn) F (in N) D(Lift(F )(N)) Elim τ D (in N) D(Lift(Elim τ D)(N)) O co tu chodzi? Niech σ(p) będzie dowolnym typem, w którym p występuje tylko pozytywnie. Skrót: µ = µp. σ(p). Operatory: in : σ(µ) µ Elim τ : (σ(τ ) τ ) µ τ wraz z regułą redukcji: Elim τ D (in N) D(Lift(Elim τ D)N) in : σ(µ) µ Elim τ : (σ(τ ) τ ) µ τ Elim τ D (in N) D(Lift(Elim τ D)N) Lift : (µ τ ) σ(µ) σ(τ ) Definiujemy funkcję F = Elim τ D typu µ τ. Dla argumentu in N, gdzie N : σ(µ), korzystamy indukcyjnie z funkcji F w kontekście Lift(F ) : σ(µ) σ(τ ). gdzie Lift : (µ τ ) σ(µ) σ(τ ) Wersja z rekursorem Rec τ : (σ(µ τ ) τ ) µ τ Rec τ D (in N σ(µ) ) D(Lift(λz µ z, Rec τ D z )N), gdzie D : σ(µ τ ) τ. Dla µ = int to jest typ 1 (int τ ) τ, izomorficzny z τ (int τ τ ). Twierdzenie Jeśli A, jest cpo, to każda funkcja ciągła f : A A ma najmniejszy punkt stały, którym jest sup{f n ( ) n N}. Dowód: Skoro f ( ) i f jest monotoniczna, więc: f ( ) f 2 ( ) f 3 ( ) Zbiór {f n ( ) n N} jest więc skierowany i z ciągłości: f (sup{f n ( ) n N}) = sup{f n+1 ( ) n N} = sup{f n ( ) n N}, czyli a = sup{f n ( ) n N} jest punktem stałym. Jeśli b jest innym punktem stałym, to z b wynika przez indukcję f n ( ) f n (b) = b, skąd a b.
Aproksymacje O co tu chodzi? Mając D : σ(τ ) τ definiujemy funkcję Typ µ = µp. σ(p) można sobie wyobrażać jako granicę/sumę ciągu przybliżeń: F = Elim τ D : µ τ jako sumę przybliżeń F k : µ k τ. µ 0 = µ 1 = σ( ) µ 2 = σ(σ( ))... Oznaczenie: µ k = σ k ( ), wtedy µ k+1 = σ(µ k ). τ σ(τ ) D τ σ(τ ) D τ Definiujemy funkcję F = Elim τ D typu µ τ też jako sumę przybliżeń F k : µ k τ. F 0 Lift(F 0 ) F 1 Lift(F 1 ) F 2 µ 0 σ(µ 0 ) in µ 1 σ(µ 1 ) in µ 2 Przejście do granicy Dla każdego k mamy diagram: A to diagram graniczny : Powrót do pierwszego pomysłu? Mamy in : σ(µ) µ Elim τ : (σ(τ ) τ ) µ τ i redukcję Elim τ D (in N) D(Lift(Elim τ D)N) σ(τ ) D τ σ(τ ) D τ Czy umiemy zdefiniować operację out : µ σ(µ)? Lift(F k ) σ(µ k ) F k+1 in µ k+1 Lift(F ) σ(µ) F in µ Można tak: out = Elim σ(µ) (Lift(in)), gdzie Lift(in) : σ(σ(µ)) σ(µ). Ale out in id : out(in N) Lift(in)(Lift(out)(N)) Równanie F in = D Lift(F ), out in = In Out czyli Elim τ D (in N) = D(Lift(Elim τ D)N) Twierdzenie Jeśli A, jest kratą zupełną, to każda monotoniczna funkcja f : A A ma najmniejszy punkt stały. Dowód: Niech B = {x A f (x) x}; niech a = inf B. Pokażemy, że a jest najmniejszym punktem stałym funkcji f. Dla dowolnego x B mamy a x, więc f (a) f (x) x. Zatem f (a) jest ograniczeniem dolnym zbioru B, skąd f (a) a, bo a jest kresem dolnym. Ale skoro f (a) a, to także f (f (a)) f (a), więc f (a) B. Zatem a f (a) i mamy równość. Ponieważ wszystkie punkty stałe funkcji f muszą należeć do B, więc a jest najmniejszym punktem stałym. Reprezentacja w systemie F W logice drugiego rzędu można definiować punkty stałe. LFP Φ (a) := R ((Φ(R) R) Ra) Po wytarciu to wygląda tak: µr. σ(r) = r ((σ(r) r) r). Okazuje się, że to jest dobra definicja µr. σ(r). µr. σ(r) = r ((σ(r) r) r) Włożenie in : σ(µ) σ Iterator Elim τ : (σ(τ ) τ ) µ τ Redukcja Elim τ D (in N) D(Lift(Elim τ D)N) Definiujemy to w systemie F: Elim = Λrλy σ(r) r λz µ. zry in = λx σ(µ) Λqλy σ(q) q. y(lift(elimqy)x), gdzie Lift : (µ q) σ(µ) σ(q) Ćwiczenie: policzyć, że przy tej definicji Elim τ D (in N) β D(Lift(Elim τ D)N) Problem otwarty Wiadomo, że w systemie F nie można definiować rekursorów używając beta-redukcji. Nie wiadomo, czy w systemie F można zdefiniować dowolny rekursor używając beta-eta-redukcji.
Wada kodowania impredykatywnego Wada kodowania impredykatywnego Typy kodowane w systemie F mają za dużo elementów. Najprostszy przykład: α β = p((α β p) p). Niech α = r(r p(p r) r) β = r(r r). Wtedy K : α oraz I : β. Dla x : α β p wyrażenie xki ma typ p. Zatem taki inhabitant typu α β Λpλx α β p. x(λrλy r λz p(p r). zp(xki))i nie jest parą M, N = ΛpλxMN, bo x FV (M) Nie można udowodnić zasady indukcji, nawet dla typu Bool. Typy ściśle pozytywne Zalety ścisłej pozytywności Nieścisła definicja ścisłej pozytywności: typ µp. σ(p) jest dozwolony tylko wtedy, gdy p nie występuje w σ(p) w argumencie implikacji. Przykład 1: ten typ nie jest ściśle pozytywny: µp. 0 (p 0) 0 Liczby Parigota: 0 = λxf.x, 1 = λxf.f 0, 2 = λxf.1,... Przykład 2: ten typ jest ściśle pozytywny: ω-tree = µp. 1 (int p). Takie typy najczęściej występują w praktyce; Łatwiejsza metateoria (np. dowodzenie SN); Mniejsze ryzyko paradoksu: typ µp ((p Bool) Bool) jest niebezpiecznie podobny do A P(P(A)). Naturalne uogólnienie na typy zależne. Typy indukcyjne Przykłady Typowa sytuacja: typ rekurencyjny jest zadany przez kilka konstruktorów (kilka schematów postaci kanonicznych), np: Zero 0 : int i następnik s : int int, dla int 1 int; nil : list i cons : int list list, dla list 1 int list; leaf : ω-tree oraz node : int ω-tree ω-tree, dla ω-tree 1 (int ω-tree). Inductive nat : Set := O : nat S : nat -> nat Inductive list (A : Type) : Type := nil : list A cons : A -> list A -> list A Typy indukcyjne nie muszą być rekurencyjne: Inductive bool : Set := Inductive unit : Set := true : bool false : bool tt : unit Inductive empty_set : Set := Spójniki zdaniowe: Inductive and (A B : Prop) : Prop := conj : A -> B -> A /\ B Inductive or (A B : Prop) : Prop := or_introl : A -> A \/ B or_intror : B -> A \/ B Inductive False : Prop := Rodziny indukcyjne (typy indukcyjne zależne) Inductive array : nat -> Set := pusty : array 0 kons : forall n:nat, nat -> array n -> array(s n) Kwantyfikator szczegółowy: Inductive ex (A : Type) (P : A -> Prop) : Prop := ex_intro : forall x : A, P x -> ex P Inductive trojki : nat -> Set := puste : trojki 0 jeszcze : forall n : nat, nat * nat * nat-> trojki n -> trojki (n + 3) Piszemy exists x:a,px zamiast ex(fun x:a => P x).
Predykaty indukcyjne Inductive even : nat -> Prop := baza : even 0 nast : forall n : nat, even n -> even (n + 2) Inductive eq (A : Type) (x : A) : A -> Prop := eq_refl : x = x Przyjęta notacja: x = A y.