logicaffeine_kernel/
term.rs

1//! Unified term representation for the Calculus of Constructions.
2//!
3//! In CoC, there is no distinction between terms and types.
4//! Everything is a Term in an infinite hierarchy of universes.
5
6use std::fmt;
7
8/// Primitive literal values.
9///
10/// These are opaque values that compute via hardware ALU, not recursion.
11#[derive(Debug, Clone, PartialEq)]
12pub enum Literal {
13    /// 64-bit signed integer
14    Int(i64),
15    /// 64-bit floating point
16    Float(f64),
17    /// UTF-8 string
18    Text(String),
19}
20
21impl Eq for Literal {}
22
23impl fmt::Display for Literal {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        match self {
26            Literal::Int(n) => write!(f, "{}", n),
27            Literal::Float(x) => write!(f, "{}", x),
28            Literal::Text(s) => write!(f, "{:?}", s),
29        }
30    }
31}
32
33/// Universe levels in the type hierarchy.
34///
35/// The hierarchy is: Prop : Type 1 : Type 2 : Type 3 : ...
36///
37/// - `Prop` is the universe of propositions (proof-irrelevant in full CIC)
38/// - `Type(n)` is the universe of types at level n
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub enum Universe {
41    /// Prop - the universe of propositions
42    Prop,
43    /// Type n - the universe of types at level n
44    Type(u32),
45}
46
47impl Universe {
48    /// Get the successor universe: Type n → Type (n+1)
49    pub fn succ(&self) -> Universe {
50        match self {
51            Universe::Prop => Universe::Type(1),
52            Universe::Type(n) => Universe::Type(n + 1),
53        }
54    }
55
56    /// Get the maximum of two universes (for Pi type formation)
57    pub fn max(&self, other: &Universe) -> Universe {
58        match (self, other) {
59            (Universe::Prop, u) | (u, Universe::Prop) => u.clone(),
60            (Universe::Type(a), Universe::Type(b)) => Universe::Type((*a).max(*b)),
61        }
62    }
63
64    /// Check if this universe is a subtype of another (cumulativity).
65    ///
66    /// Subtyping rules:
67    /// - Prop ≤ Type(i) for all i
68    /// - Type(i) ≤ Type(j) if i ≤ j
69    /// - Type(i) is NOT ≤ Prop
70    pub fn is_subtype_of(&self, other: &Universe) -> bool {
71        match (self, other) {
72            // Prop ≤ anything (Prop ≤ Prop, Prop ≤ Type(i))
73            (Universe::Prop, _) => true,
74            // Type(i) ≤ Type(j) if i ≤ j
75            (Universe::Type(i), Universe::Type(j)) => i <= j,
76            // Type(i) is NOT ≤ Prop
77            (Universe::Type(_), Universe::Prop) => false,
78        }
79    }
80}
81
82/// Unified term representation.
83///
84/// Every expression in CoC is a Term:
85/// - `Sort(u)` - universes (Type 0, Type 1, Prop)
86/// - `Var(x)` - variables
87/// - `Pi` - dependent function types: Π(x:A). B
88/// - `Lambda` - functions: λ(x:A). t
89/// - `App` - application: f x
90#[derive(Debug, Clone, PartialEq, Eq)]
91pub enum Term {
92    /// Universe: Type n or Prop
93    Sort(Universe),
94
95    /// Local variable reference (bound by λ or Π)
96    Var(String),
97
98    /// Global definition (inductive type or constructor)
99    Global(String),
100
101    /// Dependent function type: Π(x:A). B
102    ///
103    /// When B doesn't mention x, this is just A → B.
104    /// When B mentions x, this is a dependent type.
105    Pi {
106        param: String,
107        param_type: Box<Term>,
108        body_type: Box<Term>,
109    },
110
111    /// Lambda abstraction: λ(x:A). t
112    Lambda {
113        param: String,
114        param_type: Box<Term>,
115        body: Box<Term>,
116    },
117
118    /// Application: f x
119    App(Box<Term>, Box<Term>),
120
121    /// Pattern matching on inductive types.
122    ///
123    /// `match discriminant return motive with cases`
124    ///
125    /// - discriminant: the term being matched (must have inductive type)
126    /// - motive: λx:I. T — describes the return type
127    /// - cases: one case per constructor, in definition order
128    Match {
129        discriminant: Box<Term>,
130        motive: Box<Term>,
131        cases: Vec<Term>,
132    },
133
134    /// Fixpoint (recursive function).
135    ///
136    /// `fix name. body` binds `name` to itself within `body`.
137    /// Used for recursive definitions like addition.
138    Fix {
139        /// Name for self-reference within the body
140        name: String,
141        /// The body of the fixpoint (typically a lambda)
142        body: Box<Term>,
143    },
144
145    /// Primitive literal value.
146    ///
147    /// Hardware-native values like i64, f64, String.
148    /// These compute via CPU ALU, not recursion.
149    Lit(Literal),
150
151    /// Hole (implicit argument).
152    ///
153    /// Represents an argument that should be inferred by the type checker.
154    /// Used in Literate syntax like `X equals Y` where the type of X/Y is implicit.
155    Hole,
156}
157
158impl fmt::Display for Universe {
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160        match self {
161            Universe::Prop => write!(f, "Prop"),
162            Universe::Type(n) => write!(f, "Type{}", n),
163        }
164    }
165}
166
167impl fmt::Display for Term {
168    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169        match self {
170            Term::Sort(u) => write!(f, "{}", u),
171            Term::Var(name) => write!(f, "{}", name),
172            Term::Global(name) => write!(f, "{}", name),
173            Term::Pi {
174                param,
175                param_type,
176                body_type,
177            } => {
178                // Use arrow notation for non-dependent functions (param = "_")
179                if param == "_" {
180                    write!(f, "{} -> {}", param_type, body_type)
181                } else {
182                    write!(f, "Π({}:{}). {}", param, param_type, body_type)
183                }
184            }
185            Term::Lambda {
186                param,
187                param_type,
188                body,
189            } => {
190                write!(f, "λ({}:{}). {}", param, param_type, body)
191            }
192            Term::App(func, arg) => {
193                // Arrow types (Pi with _) need inner parens when used as args
194                let arg_needs_inner_parens =
195                    matches!(arg.as_ref(), Term::Pi { param, .. } if param == "_");
196
197                if arg_needs_inner_parens {
198                    write!(f, "({} ({}))", func, arg)
199                } else {
200                    write!(f, "({} {})", func, arg)
201                }
202            }
203            Term::Match {
204                discriminant,
205                motive,
206                cases,
207            } => {
208                write!(f, "match {} return {} with ", discriminant, motive)?;
209                for (i, case) in cases.iter().enumerate() {
210                    if i > 0 {
211                        write!(f, " | ")?;
212                    }
213                    write!(f, "{}", case)?;
214                }
215                Ok(())
216            }
217            Term::Fix { name, body } => {
218                write!(f, "fix {}. {}", name, body)
219            }
220            Term::Lit(lit) => {
221                write!(f, "{}", lit)
222            }
223            Term::Hole => {
224                write!(f, "_")
225            }
226        }
227    }
228}