I would like to warn you that dependent product type is usually used for the dependent function type, while the dependent version of the product type is referred to as the dependent sum type. Personally, I would try my best to avoid confusions, so I use the names "Pi types" and "Sigma types".
I wrote this answer to learn some Lean4, as I've never used Lean4 before. I appreciate nitpicks on my code.
Disjoint union
This is a disjoint union of the type A
and B
, represented using a Sigma type:
def DisjointUnion (A B : Type) : Bool → Type
| true => A
| false => B
def disjoint_union (A B : Type) :=
(b : Bool) × DisjointUnion A B b
To pattern match on a disjoint union defined above, we use Bool
as the "type tag" of usual disjoint unions:
def use_disjoint_union : (disjoint_union Bool Nat) → Nat
| ⟨true, true⟩ => 0
| ⟨true, false⟩ => 1
| ⟨false, n⟩ => n -- `n` is of type `Nat`, so this checks
Note how the second component of the sigma type vary by the first component's value.
Subsets
Here's a subset of Nat
that only have numbers less than or equal to 3
:
def Subset := (a : Nat) ×' (a ≤ 3)
def subset_instance : Subset := ⟨2, Nat.le_step (Nat.le_refl 2)⟩
In other words, when you construct an instance of Subset
, you need to show a natural number and prove it to be less than or equal to 3
. This is, in my understanding, similar to defining a subset using an injective map.
This is also an existential quantification: that there exists natural numbers less than or equal to 3
. You may replace the right hand side of ×
with any proposition about a
to make your own favorite existential quantification.
Here's a minor addition: if you want to understand the sigma types in the Agda code, for example Σ ℕ (λ x → x < n)
this expression, you split it into three parts with the Σ symbol ignored: ℕ
, λ x →
, and x < n
; and then you reform it as (x : ℕ) × (x < n)
.
(t : Type) × t
. You can read that as “a pair of a typet
together with a value of typet
”. $\endgroup$