I am playing around the Programming Language Foundations in Agda exercises and wondering if we can achieve the same level of theories with dependent types. With simple types, the definition usually goes like the following:
data Type : Set where -- well formed types
...
_→̇_ : Type → Type → Type
Context = List Type
data _∋_ : Context → Type → Set where -- De Bruijn indices
here : {Γ : Context} → {A : Type}
→ Γ ‚ A ∋ A
there : {Γ : Context} → {A B : Type}
→ Γ ∋ B
→ Γ ‚ A ∋ B
data _⊢_ : Context → Type → Set where -- well formed terms
lookup : {Γ : Context} → {A : Type} -- variables
→ Γ ∋ A
→ Γ ⊢ A
λ̇_ : {Γ : Context} → {A B : Type}
→ Γ ‚ A ⊢ B
→ Γ ⊢ A →̇ B
_·_ : {Γ : Context} → {A B : Type}
→ Γ ⊢ A →̇ B
→ Γ ⊢ A
→ Γ ⊢ B
...
On the other hand, dependent types usually use mutual inductive-inductive/recursive definitions that goes like the following (sorry if I miss any constructors, and usage of the dot notation):
data Context : Set
data Substitution : Context → Context → Set
data Type : Context → Set
data _⊢_ : (Γ : Context) → Type Γ → Set
data Context where
[] : Context
_‚_ : (Γ : Context) → Type Γ → Context
data Type where
Ṡet : {Γ : Context} → Type Γ
El : {Γ : Context} → Γ ⊢ Ṡet → Type Γ
_→̇_ : {Γ : Context} → (A : Type Γ) → Type (Γ ‚ A) → Type Γ
substitute-type : {Γ Δ : Context} → Substitution Δ Γ → Type Γ → Type Δ
data Substitution where
[] : {Δ : Context} → Substitution Δ []
_‚_ : {Γ Δ : Context} → {A : Type Γ} → (σ : Substitution Δ Γ) → (Δ ⊢ substitute-type σ A) → Substitution Δ (Γ ‚ A)
id : {Γ : Context} → Substitution Γ Γ
_∘_ : {Γ Δ Ε : Context} → Substitution Δ Γ → Substitution Ε Δ → Substitution Ε Γ
p₁ : {Γ Δ : Context} → {A : Type Γ} → Substitution Δ (Γ ‚ A) → Substitution Δ Γ
shift : {Γ : Context} → {A : Type Γ} → Substitution (Γ ‚ A) Γ
shift = p₁ id
data _⊢_ where
here : {Γ : Context} → {A : Type Γ}
→ Γ ‚ A ⊢ substitute-type shift A
substitute-term : {Γ Δ : Context} → (σ : Substitution Δ Γ) → {A : Type Γ} → (Γ ⊢ A) → (Δ ⊢ substitute-type σ A)
p₂ : {Γ Δ : Context} → {A : Type Γ} → (σ : Substitution Δ (Γ ‚ A)) → (Δ ⊢ substitute-type (p₁ σ) A)
λ̇_ : {Γ : Context} → {A : Type Γ} → {B : Type (Γ ‚ A)}
→ Γ ‚ A ⊢ B
→ Γ ⊢ A →̇ B
ȧpp : {Γ : Context} → {A : Type Γ} → {B : Type (Γ ‚ A)}
→ Γ ⊢ A →̇ B
→ Γ ‚ A ⊢ B
I found a few papers about this subject (see ref section of https://ncatlab.org/nlab/show/internal+type+theory, as well as Type Theory in Type Theory using Quotient Inductive Types, Normalisation by Evaluation for Dependent Types, and many others). All of the implementation seems to use Explicit Substitution. I think the usage is due to the context category in the category with families/attributes models, but on syntactical level, can we show substitution must be a first party operation (as some sort of dependent list of types) instead of a meta-theoretical operation for dependent types? I tried to define it but it goes into a circular definition (since the dependent list of types uses the action of substitution on types). Is it so happens that we can define substitution in the meta-theory (i.e., agda) in the simple type case?