Skip to content

Instantly share code, notes, and snippets.

@EduardoRFS
Last active July 9, 2025 17:32
Show Gist options
  • Save EduardoRFS/1fc25bd5a03eb51d72ac8eb23e535c42 to your computer and use it in GitHub Desktop.
Save EduardoRFS/1fc25bd5a03eb51d72ac8eb23e535c42 to your computer and use it in GitHub Desktop.

Pi-Sigma-Self

The following is a set of syntax-directed rules to typing a dependently type system with self types. This is hopefully decidable given decidable conversion checking.

The main innovation is that all introduction rules propagate the self due to the new type of checking rule. This allows the fixpoint to be transparent to introductions, when combined with pairs, this leads to mutual recursion and the inductive types.

This doesn't define any universe or restriction to fixpoints, as those are more or less orthogonal to the typing issues here.

Syntax

Term (M, N, K) ::=
  | (M : A) // annot-type
  | (M == N) // annot-equal
  | x | x = M; K // vars
  | (x : A) -> B | M(N) | (x) => M // functions
  | [x : A, B] | M.0 | M.1 | [x = M, N] // pairs
  | @(x) -> M | M.@ | @(x) => M // fixpoints

Type (A, B, C) ::= Term
// TODO: better letter, as S is used for Sort
Self (S) ::= Term

Reductions

Those reductions are typed, to help to visualize subject reduction. The main attention point here is the additional equality produced by unfold, which is why the equality needs to be explicitly added to the system.

// typed reductions at a distance alá Linear Substitution Calculus
// https://inria.hal.science/hal-00780344v1/document

x = (M : A); C<x> |-> x = (M : A); C<(M : A)>            // subst
x = (M : A); K |-> K                        (x ∉ fv(M))  // gc

L<(x : A) => K>(N) |-> L<x = (N : A); K>                 // beta
L<[x = (M : A), (N : B)]>.0 |-> L<(M : A)>               // fst
L<[x = (M : A), (N : B)]>.1 |-> L<(N : B)>               // snd
L<@(x : A) => M>.@ |-> L<x = @(x : A) => M; (M == x.@)>  // unfold

Typing

// rules
Γ |- M(A : Type) // infer
Γ |- M(A : Type) // check
Γ | (S : A) |- M(A : Type) // check-with-self

// core
Γ |- TType  Γ |- MT
------------------------- // infer-annot
Γ |- (M : T)T

Γ |- NT  Γ | N |- MT  Γ |- M == N
--------------------------------------- // infer-equal
Γ |- (M == N)T

Γ, x : T | x |- MT
--------------------- // check-with-any-self
Γ |- MT

Γ |- MT
-------------- // check-if-infer
Γ | S |- MT

// vars
----------------- // var
Γ, x : A |- x ⇒ A

Γ |- MA  Γ |- K{x := M}B
------------------------------ // let-infer
Γ |- x = M; KB

Γ |- MA  Γ |- K{x := M}B
------------------------------ // let-check
Γ |- x = M; KB

// functions
Γ |- AType  Γ, x : A |- BType
----------------------------------- // function-forall
Γ |- (x : A) -> BType

Γ |- M(x : A) -> B  Γ |- NA
--------------------------------- // function-apply
Γ |- M(N)B{x := N}

Γ, x : A | S(x) |- MB
-------------------------------- // function-lambda
Γ | S |- (x) => M(x : A) -> B

// pairs
Γ |- AType  Γ, x : A |- BType
----------------------------------- // pairs-exists
Γ |- [x : A, B]Type

Γ |- M[x : A, B]
------------------- // pairs-fst
Γ |- M.0A

Γ |- M[x : A, B]
---------------------- // pairs-snd
Γ |- M.1B{x := M.0}

Γ | S.0 |- MA  Γ | S.1 |- N{x := M}B{x := N}
-------------------------------------------------- // pairs-intro
Γ | S |- [x = M, N][x : A, B]

// fixpoints
Γ, x : S |- TType
------------------------- // self-type
Γ | S |- @(x) -> TType

Γ |- M ⇒ @(x) -> T
------------------- // self-unfold
Γ |- M.@ ⇒ T{x := M}

Γ | S.@ |- M{x := S}T
------------------------------ // self-fix
Γ | S |- @(x) => M ⇐ @(x) -> T
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment