DCC - The Dependent Combinator Calculus

Overview

The DCC is a dependently-typed calculus based on categorical combinators. DCC is extensional, meaning the equality reflection theorem (proof) holds. DCC has decidable type-checking (proof), is strongly-normalizing (proof), and consistent (proof). A subset of Lean 4 can be compiled to DCC.

DCC is quite minimal, aiming to have sound metatheory. It is implemented entirely in Lean with a type-checker with soundness and completeness. You can import the type-checker (Expr.infer) by importing Dcc.Judge3. The β-reduction engine can be imported with import Dcc.Red.

See the paper for a formal presentation of DCC.

Examples

DCC uses a Lisp-like S-expression syntax. A DCC program looks like this:

(DCC ((Λ (Sort 0)
         (Id (Sort 0))
         (Id (Σ (Sort 0)
             (Id (Sort 0)))))
      𝟙
      ∗))

This is a program that applies a type 𝟙 (the top / unit type) and its inhabitant ∗ to a function which accepts a type and an inhabitant of its type (Λ (Sort 1) (Id (Sort 0)) (Id (Σ (Sort 0) (Id (Sort 0))))). Its output is as such:

open Expr in
#eval reduce_all (DCC ((Λ (Sort 0) (Id (Sort 0)) (Id (Σ (Sort 0) (Id (Sort 0))))) 𝟙 ∗))
/-
Messages here:
16:0:
(DCC (,(Id (Sort 0)) 𝟙 ∗))
-/

It type-checks as such:

open Expr in
#eval (·.fst) <$> infer (DCC ((Λ (Sort 0) (Id (Sort 0)) (Id (Σ (Sort 0) (Id (Sort 0))))) 𝟙 ∗))
/-
Messages here:
16:0:
Except.ok (DCC (Σ (Sort 0) (Id (Sort 0))))
-/

Boolean negation

This is a function that flips boolean:

(DCC (𝟚.Rec (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟚))
            (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟘₂))
            (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟙₂))))

It type-checks as such:

open Dsl Expr in
#eval (·.fst) <$> infer (DCC (𝟚.Rec (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟚)) (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟘₂)) (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟙₂))))
/-
Messages here:
191:0:
Except.ok (DCC ((Π (`𝟙 𝟙) (`𝟙 (Π (`𝟙 𝟚) (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟚))))) ∗))
-/

It runs as such:

open Dsl Expr in
#eval reduce_all (DCC ((𝟚.Rec (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟚)) (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟘₂)) (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟙₂))) ∗ 𝟙₂))
/-
Messages here:
191:0:
(DCC 𝟘₂)
-/

And type-checks correctly:

open Dsl Expr in
#eval (·.fst) <$> infer (DCC ((𝟚.Rec (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟚)) (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟘₂)) (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟙₂))) ∗ 𝟙₂))
/-
Messages here:
191:0:
Except.ok (DCC 𝟚)
-/

Bool-indexed identity

This is a DCC expression for the identity function indexed by a boolean. If it is applied 𝟙₂, its domain is A. If it applied 𝟘₂, its domain is B:

def example_curr_bool_select (A B : Expr) : Expr :=
  -- If true, 𝟙, if false 𝟘
  let motive     := DCC (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) (Sort 0)))
  let case_true  := DCC (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) A))
  let case_false := DCC (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) B))
  let selector   := (DCC ((𝟚.Rec motive case_true case_false) ∗))
  DCC (Λ 𝟚 selector (Snd selector))

open Dsl in
#eval (·.fst) <$> Expr.infer (example_curr_bool_select (DCC 𝟙) (DCC 𝟘))
/-
Messages here:
213:0:
Except.ok (DCC
 ((Π (`𝟙 𝟚)
     (`𝟙 (Π ((𝟚.Rec (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) (Sort 0))) (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟙)) (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟘)))
             ∗)
            (Λ 𝟚
               ((𝟚.Rec (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) (Sort 0))) (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟙))
                       (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟘)))
                ∗)
               (∘ ((𝟚.Rec (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) (Sort 0))) (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟙))
                          (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟘))) ∗)
                  (Fst ((𝟚.Rec (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) (Sort 0))) (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟙))
                               (Λ 𝟙 (`𝟙 𝟚) (`(Σ 𝟙 (`𝟙 𝟚)) 𝟘))) ∗)))))))
  ∗))
-/

open Dsl in
#eval (·.fst) <$> Expr.infer (DCC (#((example_curr_bool_select (DCC 𝟙) (DCC 𝟘))) 𝟙₂ ∗))
/-
Messages here:
215:0:
Except.ok (DCC 𝟙)
-/

open Dsl in
#eval Expr.reduce_all (DCC (#((example_curr_bool_select (DCC 𝟙) (DCC 𝟘))) 𝟙₂ ∗))
/-
Messages here:
218:0:
(DCC ∗)
-/

Lean Compiler

The current version of DCC does not have η-reduction, so some examples of the Lean compiler do not work. However, here is the identity function on 𝟚:

#eval Std.format (dcc_compile (fun (x : Bool) => x))
/-
Messages here:
24:0:
((!Λ 𝟙 (`𝟙 𝟚) (∘ (Snd (`𝟙 𝟚)) (* (Σ 𝟙 (`𝟙 𝟚))))) (& ∗))
-/

It type-checks as such:

#eval (·.fst) <$> Expr.infer (dcc_compile (fun (x : Bool) => x))
Messages here:
25:0:
Except.ok (DCC ((Π (!<> (`𝟙 𝟚)) (!Λ 𝟙 (`𝟙 𝟚) (∘ (∘ (`𝟙 𝟚) (Fst (`𝟙 𝟚))) (* (Σ 𝟙 (`𝟙 𝟚)))))) (& ∗)))

Past versions of DCC had more powerful η-reduction. However, we have omitted it in this version so as to achieve these metatheoretic results:

We hope to add back η-reduction and more powerful type conversion in a later paper.

Lean Toolchain

I am using Lean4 with the toolchain leanprover/lean4:v4.30.0-rc2. Lean support with Nix is not well-maintained, so you will have to install it manually.

By Lexi Aiello.