Logical Foundations notes Coq + Lean
1 Understanding Coq and Lean
1.1 Lean variables vs constants
- variables mean free variables
P Q : Prop variables
1.2 Inductive dependent types
inductive nat: Type
is a plain inductive typeinductive nat (k: Type) : Type
is a dependent inductive type `inductive nat : Type -> Type
is same as above- whenever our
Inductive
associates with function type, it indicates we are using a dependent typeInductive binary_word1 (n:nat) : Type
means thatbinary_word1
takes a(n:nat)
and returns a new type. This means the canonical types we see in the wild isbinary_word1 1, binary_word1 2,...
Inductive binary_word : nat -> Type
shows thatbinary_word
has a function type meaning it is a dependent type.
--Lean
--Coq (2 approaches)
Inductive binary_word1 (n:nat) : Type
:= bwc (l : list bool) (_ : length l = n).
Inductive binary_word : nat -> Type :=
| bw_nil : binary_word 0
| bw_cons : forall n, bool -> binary_word n -> binary_word (S n).
1.3 Coq vs Lean
1.3.1 Inductive type
- Type formation diff - Coq requires specifying output type, it is optional in Lean
- Lean
inductive prod (A B : Type) : Type
inductive prod (A B : Type)
Lean can also omit the output type
- Coq
Inductive prod (A B : Type) : Type
- Lean
- Term introduction/ constructors - Lean makes the type implicit, Coq makes it explicit
- Lean
mk 2 4 :: prod nat nat
- Coq
mk nat nat 2 4 :: prod nat nat
- Lean
namespace hidden A B : Type) : Type
inductive prod (| mk : A → B → prod
open prod#check mk 2 4
-- mk 2 4 :: prod nat nat
A B : Type) --Output type omitted
inductive prod (| mk : A → B → prod
end hidden
-- DIF--
Inductive prod (A B: Type) : Type :=
| mk (x:A) (y:B) .
Check mk nat nat 2 4. --notice how this diff from Lean's mk
--we can also add typing to our term intro/constructor
Inductive prod (A B:Type) : Type :=
| pair (x:A) (y:B) : A -> B -> prod A B.
A more closer lean analogy to coq’s prod is below
: Type -> Type -> Type 1
inductive prod | mk : Π (α β : Type), α → β → prod α β
#check mk nat nat 2 4
--mk nat nat 2 4 :: prod ℕ ℕ
#check mk
--mk :: Π (α β : Type), α → β → prod α β
which we can also mimic in coq
Inductive prod : Type -> Type -> Type :=
| mk : forall A B :Type , A -> B -> prod A B .
Check pair nat nat 2 4.
* pair nat nat 2 4 :: prod nat nat *)
(
Check mk.
* mk :: forall A B : Type, A -> B -> prod A B *) (
1.3.2 Implicit
- Notice how with
(A B : Type)
the result ofmk 2 4 :: prod A B
- But with
{A B : Type}
the result ofmk 2 4 :: prod
meaning this is not a dependent type
A B: Type} : Type
inductive prodB {| mk : A → B → prodB
#check mk 2 4
-- mk 2 4 :: prod
coq
Inductive prod {A B: Type} : Type :=
| mk (x:A) (y:B) .
Check mk 2 4.
* mk 2 4 :: prod *) (
2 Polymorphism
in C++ we have vector<int>
as the type for a list of int. For a polymorphic list, we have vector<T>
which is really a dependent type Π( T : Type*) -> vector T
Inductive natlist : Type :=
| nil
| cons (n : nat) (l : natlist).
** For example, here is a three-element list: *)
(
Definition mylist := cons 1 (cons 2 (cons 3 nil)).
Check mylist.
\[\cfrac{}{\vdash nil : natlist} \qquad \cfrac{\vdash n: nat \qquad \vdash l:natlist}{\vdash cons\ n\ l : natlist}\]
3 inductive Type, Type formation, Term intro
- fst and snd projection functions are canonical functions of the product type
- Type formation and Term introduction rules are inside the
Inductive
type keyword \[\cfrac{}{\vdash False : Prop} \tag{type formation}\] \[\text{There is no term intro for False} \tag{term intro}\]
Inductive False : Prop.
\[\cfrac{}{\vdash True : Prop} \tag{type formation}\] \[\cfrac{}{\vdash top : True} \tag{term intro}\]
Inductive True : Prop :=
| top : True.
\[\cfrac{\vdash P : Prop \qquad \vdash Q : Prop}{\vdash and\ P\ Q : Prop} \tag{type formation}\]
\[\cfrac{\vdash x : P \qquad \vdash y : Q}{\vdash conj\ x\ y\ : and\ P\ Q} \tag{term intro}\]
- term Elimination for Sum type is Pattern matching in Haskell, Coq,
\[\cfrac{\vdash p: A + B \qquad x: A \vdash v_A : C \qquad y : B \vdash v_B : C }{match(p,x.v_A,y.v_B):C} \tag{term elim}\]
Inductive and (P Q : Prop) : Prop := --Type formation
| conj : P -> Q -> and P Q. --Term introduction
Check conj True True Top Top.
- Notice we have to do
conj nat nat 2 4
NOTCheck conj 2 4.
because we actually defined a dependent type.conj :: Π(P : Prop) -> Π(Q : Prop) -> P -> Q
: Prop --no intro rules for Bottom Type
inductive falseN
: Prop --only 1 intro rule for Top
inductive trueN | introT : trueN
-- ------
-- T
: Prop) : Prop
inductive andN (a b | introConj : a -> b -> andN
-- a b
-- --------
-- a ∧ b
: Prop) : Prop
inductive orN (a b | intro_left: a -> orN
| intro_right: b -> orN
-- a b
-- -------- -------
-- a ∨ b a ∨ b
3.0.1 SUM TYPE and PRODUCT TYPES
use inductive keyword to create intro rules use def or function definitions to create elim rules
universes j k
ProdN (a : Type j) (b : Type k)
inductive | pair : a -> b -> ProdN
Either (a : Type j) (b : Type k)
inductive | inl : a -> Either
| inr : b -> Either
EitherElim : (Either nat string) -> nat
def | (inl 4) := 6
ProdN
open Either
open #check pair nat char
-- pair ℕ char : prodN Type Type
#check inl nat
--inl ℕ : Either Type ?M_1
#check@ inl nat
--inl : Π {b : Type u_1}, ℕ → Either ℕ b
in haskell
data Either a b = Left a | Right b
Left nat :: Either nat M?
3.1 What is the meaning of a proposition
- To understand meaning we need to combine duality of Verificationist and Pragmatist
- Verificationist tells us meaning is defined by how it is built.
- The meaning of a proposition is precisely the components or nodes that build it
- Verificationist owns the Intro rules
- Pragmatist tells us meaning is defined by how it is deconstructed.
- The meaning of a proposition is precisely the things we can conclude from the proposition
- Pragmatist owns the Elim rules
- Verificationist tells us meaning is defined by how it is built.
3.1.1 Harmony
- Verificationist
- intro then elim, then reduce the proof tree
- the resulting proof tree lacks elim rules
- intro then elim, then reduce the proof tree
- Pragmatist
- elim then intro, then reduce the proof tree
- the resulting proof tree lack intro rules
- elim then intro, then reduce the proof tree
4 Idiosyncrasy
- Coq will give an error for the below
Inductive and : Prop -> Prop -> Prop :=
| introConj (a: Prop) (b:Prop) : and.
- We need to push back the props
Inductive and (P Q : Prop) : Prop :=
| introConj : P -> Q -> and P Q.
5 Pi vs Lambda vs forall
A B} (f: A -> B) :=
def injective {: A, f x = f y -> x = y
Π x y
A B} (f: A -> B) :=
def injective {: A, f x = f y -> x = y
∀ x y
A B} (f: A -> B) :=
def injective {: A, f x = f y -> x = y λ x y
6 Notes
Poly
Polymorphic inductive type definition aka Inductive + Dependent types
implicit args
Because X is declared as implicit for the entire inductive definition including list’ itself, we now have to write just list’ whether we are talking about lists of numbers or booleans or anything else, rather than list’ nat or list’ bool or whatever; this is a step too far.
Tactics
All constructors/ term intro are injective aka rules that take arguments like successor takes argument n to make S n, and if S m = S n then n = m. Constructors that take no arguments are trivilaly injective like nil in list or 0 in nat.