-- -------------------------------------------------------------------------------------------------------------- -- | BASIC LOGIC | -- -------------------------------------------------------------------------------------------------------------- -- first some basic logic, see for more details on these definitions false : * := forall (A : *), A; false_elim (A : *) (contra : false) : A := contra A; not (A : *) : * := A -> false; not_intro (A : *) (h : A -> false) : not A := h; not_elim (A B : *) (a : A) (na : not A) : B := na a B; and (A B : *) : * := forall (C : *), (A -> B -> C) -> C; and_intro (A B : *) (a : A) (b : B) : and A B := fun (C : *) (H : A -> B -> C) => H a b; and_elim_l (A B : *) (ab : and A B) : A := ab A (fun (a : A) (b : B) => a); and_elim_r (A B : *) (ab : and A B) : B := ab B (fun (a : A) (b : B) => b); or (A B : *) : * := forall (C : *), (A -> C) -> (B -> C) -> C; or_intro_l (A B : *) (a : A) : or A B := fun (C : *) (ha : A -> C) (hb : B -> C) => ha a; or_intro_r (A B : *) (b : B) : or A B := fun (C : *) (ha : A -> C) (hb : B -> C) => hb b; or_elim (A B C : *) (ab : or A B) (ha : A -> C) (hb : B -> C) : C := ab C ha hb; eq (A : *) (x y : A) := forall (P : A -> *), P x -> P y; eq_refl (A : *) (x : A) : eq A x x := fun (P : A -> *) (Hx : P x) => Hx; eq_sym (A : *) (x y : A) (Hxy : eq A x y) : eq A y x := fun (P : A -> *) (Hy : P y) => Hxy (fun (z : A) => P z -> P x) (fun (Hx : P x) => Hx) Hy; eq_trans (A : *) (x y z : A) (Hxy : eq A x y) (Hyz : eq A y z) : eq A x z := fun (P : A -> *) (Hx : P x) => Hyz P (Hxy P Hx); eq_cong (A B : *) (x y : A) (f : A -> B) (H : eq A x y) : eq B (f x) (f y) := fun (P : B -> *) (Hfx : P (f x)) => H (fun (a : A) => P (f a)) Hfx; -- -------------------------------------------------------------------------------------------------------------- -- | BASIC DEFINITIONS | -- -------------------------------------------------------------------------------------------------------------- -- note we leave off the type ascriptions for most of these, as the type isn't -- very interesting -- I'd always strongly recommend including the type ascriptions for theorems -- a unary operation on a set `A` is a function `A -> A` unop (A : *) := A -> A; -- a binary operation on a set `A` is a function `A -> A -> A` binop (A : *) := A -> A -> A; -- a binary operation is associative if ... assoc (A : *) (op : binop A) := forall (a b c : A), eq A (op (op a b) c) (op a (op b c)); -- an element `e : A` is a left identity with respect to binop `op` if `∀ a, e * a = a` id_l (A : *) (op : binop A) (e : A) := forall (a : A), eq A (op e a) a; -- likewise for right identity id_r (A : *) (op : binop A) (e : A) := forall (a : A), eq A (op a e) a; -- an element is an identity element if it is both a left and right identity id (A : *) (op : binop A) (e : A) := and (id_l A op e) (id_r A op e); -- b is a left inverse for a if `b * a = e` -- NOTE: we don't require `e` to be an identity in this definition. -- this definition is purely for convenience's sake inv_l (A : *) (op : binop A) (e : A) (a b : A) := eq A (op b a) e; -- likewise for right inverse inv_r (A : *) (op : binop A) (e : A) (a b : A) := eq A (op a b) e; -- and full-on inverse inv (A : *) (op : binop A) (e : A) (a b : A) := and (inv_l A op e a b) (inv_r A op e a b); -- -------------------------------------------------------------------------------------------------------------- -- | ALGEBRAIC STRUCTURES | -- -------------------------------------------------------------------------------------------------------------- -- a set `S` with binary operation `op` is a semigroup if its operation is associative semigroup (S : *) (op : binop S) : * := assoc S op; -- a set `M` with binary operation `op` and element `e` is a monoid monoid (M : *) (op : binop M) (e : M) : * := and (semigroup M op) (id M op e); -- some "getters" for `monoid` so we don't have to do a bunch of very verbose -- and-eliminations every time we want to use something id_lm (M : *) (op : binop M) (e : M) (Hmonoid : monoid M op e) : id_l M op e := and_elim_l (id_l M op e) (id_r M op e) (and_elim_r (semigroup M op) (id M op e) Hmonoid); id_rm (M : *) (op : binop M) (e : M) (Hmonoid : monoid M op e) : id_r M op e := and_elim_r (id_l M op e) (id_r M op e) (and_elim_r (semigroup M op) (id M op e) Hmonoid); -- now we can prove that, for any monoid, if `a` is a left identity, then it -- must be "the" identity monoid_id_l_implies_identity (M : *) (op : binop M) (e : M) (Hmonoid : monoid M op e) (a : M) (H : id_l M op a) : eq M a e := -- WTS a = a * e = e -- we can use `eq_trans` to glue proofs of `a = a * e` and `a * e = e` together eq_trans M a (op a e) e -- first, `a = a * e`, but we'll use `eq_sym` to flip it around (eq_sym M (op a e) a -- now the goal is to show `a * e = a`, which follows immediately from `id_r` (id_rm M op e Hmonoid a)) -- now we need to show that `a * e = e`, but this immediately follows from `H` (H e); -- the analogous result for right identities monoid_id_r_implies_identity (M : *) (op : binop M) (e : M) (Hmonoid : monoid M op e) (a : M) (H : id_r M op a) : eq M a e := -- this time, we'll show `a = e * a = e` eq_trans M a (op e a) e -- first, `a = e * a` (eq_sym M (op e a) a -- this time, it immediately follows from `id_l` (id_lm M op e Hmonoid a)) -- and `e * a = e` (H e); -- groups are just monoids with inverses group (G : *) (op : binop G) (e : G) (i : unop G) : * := and (monoid G op e) (forall (a : G), inv G op e a (i a));