csw_derive/
derivation.rs

1//! Type system derivation from categorical structures.
2//!
3//! This module implements the core derivation logic that transforms
4//! categorical specifications into type systems.
5
6use csw_core::{CategorySpec, DiagonalSpec, TerminalSpec};
7
8use crate::{
9    EquivalenceRule, ReductionRule, StructuralRules, TermConstructor, TermConstructorKind,
10    TypeConstructor, TypeConstructorKind, TypeSystem, TypingRule,
11};
12
13/// The main derivation engine.
14///
15/// Transforms categorical specifications into type systems using
16/// the Curry-Howard-Lambek correspondence.
17pub struct Deriver;
18
19impl Deriver {
20    /// Derive a type system from a categorical specification.
21    ///
22    /// This is the main entry point for derivation.
23    ///
24    /// # Example
25    ///
26    /// ```rust
27    /// use csw_core::CategoryBuilder;
28    /// use csw_derive::Deriver;
29    ///
30    /// let ccc = CategoryBuilder::new("STLC")
31    ///     .with_terminal()
32    ///     .with_products()
33    ///     .with_exponentials()
34    ///     .cartesian()
35    ///     .build()
36    ///     .unwrap();
37    ///
38    /// let ts = Deriver::derive(&ccc);
39    /// assert_eq!(ts.name, "STLC");
40    /// ```
41    pub fn derive(spec: &CategorySpec) -> TypeSystem {
42        let mut ts = TypeSystem {
43            name: spec.name.clone(),
44            structural: StructuralRules {
45                weakening: matches!(spec.structure.terminal_morphism, TerminalSpec::Universal),
46                contraction: matches!(spec.structure.diagonal, DiagonalSpec::Universal),
47                exchange: spec.structure.symmetry,
48            },
49            ..Default::default()
50        };
51
52        // Always add variable rule
53        Self::derive_variable(&mut ts);
54
55        // Add base types
56        for obj in &spec.base_objects {
57            ts.type_constructors.push(TypeConstructor {
58                name: obj.name.clone(),
59                symbol: obj.name.clone(),
60                arity: 0,
61                kind: TypeConstructorKind::Base,
62            });
63        }
64
65        // Derive from structural features
66        if spec.structure.terminal {
67            Self::derive_terminal(&mut ts);
68        }
69
70        if spec.structure.initial {
71            Self::derive_initial(&mut ts);
72        }
73
74        if spec.structure.products {
75            Self::derive_products(&mut ts, spec);
76        }
77
78        if spec.structure.coproducts {
79            Self::derive_coproducts(&mut ts);
80        }
81
82        if spec.structure.exponentials {
83            Self::derive_exponentials(&mut ts);
84        }
85
86        if spec.structure.tensor.is_some() {
87            Self::derive_tensor(&mut ts, spec);
88        }
89
90        if spec.structure.linear_hom {
91            Self::derive_linear_hom(&mut ts);
92        }
93
94        ts
95    }
96
97    fn derive_variable(ts: &mut TypeSystem) {
98        ts.term_constructors.push(TermConstructor {
99            name: "var".to_string(),
100            symbol: "x".to_string(),
101            kind: TermConstructorKind::Variable,
102        });
103
104        ts.typing_rules.push(TypingRule {
105            name: "var".to_string(),
106            premises: vec![],
107            conclusion: "Γ, x:A ⊢ x : A".to_string(),
108            side_conditions: vec![],
109        });
110    }
111
112    fn derive_terminal(ts: &mut TypeSystem) {
113        // Type: 1 (Unit)
114        ts.type_constructors.push(TypeConstructor {
115            name: "Unit".to_string(),
116            symbol: "1".to_string(),
117            arity: 0,
118            kind: TypeConstructorKind::Unit,
119        });
120
121        // Term: ()
122        ts.term_constructors.push(TermConstructor {
123            name: "unit".to_string(),
124            symbol: "()".to_string(),
125            kind: TermConstructorKind::UnitIntro,
126        });
127
128        // Rule: Γ ⊢ () : 1
129        ts.typing_rules.push(TypingRule {
130            name: "unit-intro".to_string(),
131            premises: vec![],
132            conclusion: "Γ ⊢ () : 1".to_string(),
133            side_conditions: vec![],
134        });
135
136        // η-rule: e : 1 ⟹ e ≡ ()
137        ts.equivalence_rules.push(EquivalenceRule {
138            name: "unit-eta".to_string(),
139            lhs: "e".to_string(),
140            rhs: "()".to_string(),
141            condition: Some("e : 1".to_string()),
142        });
143    }
144
145    fn derive_initial(ts: &mut TypeSystem) {
146        // Type: 0 (Empty)
147        ts.type_constructors.push(TypeConstructor {
148            name: "Empty".to_string(),
149            symbol: "0".to_string(),
150            arity: 0,
151            kind: TypeConstructorKind::Empty,
152        });
153
154        // Term: absurd
155        ts.term_constructors.push(TermConstructor {
156            name: "absurd".to_string(),
157            symbol: "absurd".to_string(),
158            kind: TermConstructorKind::Absurd,
159        });
160
161        // Rule: Γ ⊢ e : 0 ⟹ Γ ⊢ absurd e : A
162        ts.typing_rules.push(TypingRule {
163            name: "absurd-elim".to_string(),
164            premises: vec!["Γ ⊢ e : 0".to_string()],
165            conclusion: "Γ ⊢ absurd e : A".to_string(),
166            side_conditions: vec![],
167        });
168    }
169
170    fn derive_products(ts: &mut TypeSystem, spec: &CategorySpec) {
171        // Type: A × B
172        ts.type_constructors.push(TypeConstructor {
173            name: "Product".to_string(),
174            symbol: "×".to_string(),
175            arity: 2,
176            kind: TypeConstructorKind::Product,
177        });
178
179        // Terms: (a, b), fst, snd
180        ts.term_constructors.push(TermConstructor {
181            name: "pair".to_string(),
182            symbol: "(_, _)".to_string(),
183            kind: TermConstructorKind::PairIntro,
184        });
185        ts.term_constructors.push(TermConstructor {
186            name: "fst".to_string(),
187            symbol: "fst".to_string(),
188            kind: TermConstructorKind::PairElimFst,
189        });
190        ts.term_constructors.push(TermConstructor {
191            name: "snd".to_string(),
192            symbol: "snd".to_string(),
193            kind: TermConstructorKind::PairElimSnd,
194        });
195
196        // Typing rules
197        ts.typing_rules.push(TypingRule {
198            name: "pair-intro".to_string(),
199            premises: vec!["Γ ⊢ a : A".to_string(), "Γ ⊢ b : B".to_string()],
200            conclusion: "Γ ⊢ (a, b) : A × B".to_string(),
201            side_conditions: vec![],
202        });
203
204        ts.typing_rules.push(TypingRule {
205            name: "fst-elim".to_string(),
206            premises: vec!["Γ ⊢ p : A × B".to_string()],
207            conclusion: "Γ ⊢ fst p : A".to_string(),
208            side_conditions: vec![],
209        });
210
211        ts.typing_rules.push(TypingRule {
212            name: "snd-elim".to_string(),
213            premises: vec!["Γ ⊢ p : A × B".to_string()],
214            conclusion: "Γ ⊢ snd p : B".to_string(),
215            side_conditions: vec![],
216        });
217
218        // β-rules
219        ts.reduction_rules.push(ReductionRule {
220            name: "fst-beta".to_string(),
221            lhs: "fst (a, b)".to_string(),
222            rhs: "a".to_string(),
223        });
224        ts.reduction_rules.push(ReductionRule {
225            name: "snd-beta".to_string(),
226            lhs: "snd (a, b)".to_string(),
227            rhs: "b".to_string(),
228        });
229
230        // η-rule (only if cartesian - has diagonal)
231        if matches!(spec.structure.diagonal, DiagonalSpec::Universal) {
232            ts.equivalence_rules.push(EquivalenceRule {
233                name: "pair-eta".to_string(),
234                lhs: "p".to_string(),
235                rhs: "(fst p, snd p)".to_string(),
236                condition: Some("p : A × B".to_string()),
237            });
238        }
239    }
240
241    fn derive_coproducts(ts: &mut TypeSystem) {
242        // Type: A + B
243        ts.type_constructors.push(TypeConstructor {
244            name: "Coproduct".to_string(),
245            symbol: "+".to_string(),
246            arity: 2,
247            kind: TypeConstructorKind::Coproduct,
248        });
249
250        // Terms: inl, inr, case
251        ts.term_constructors.push(TermConstructor {
252            name: "inl".to_string(),
253            symbol: "inl".to_string(),
254            kind: TermConstructorKind::InjLeft,
255        });
256        ts.term_constructors.push(TermConstructor {
257            name: "inr".to_string(),
258            symbol: "inr".to_string(),
259            kind: TermConstructorKind::InjRight,
260        });
261        ts.term_constructors.push(TermConstructor {
262            name: "case".to_string(),
263            symbol: "case _ of inl x ⇒ _ | inr y ⇒ _".to_string(),
264            kind: TermConstructorKind::Case,
265        });
266
267        // Typing rules
268        ts.typing_rules.push(TypingRule {
269            name: "inl-intro".to_string(),
270            premises: vec!["Γ ⊢ a : A".to_string()],
271            conclusion: "Γ ⊢ inl a : A + B".to_string(),
272            side_conditions: vec![],
273        });
274
275        ts.typing_rules.push(TypingRule {
276            name: "inr-intro".to_string(),
277            premises: vec!["Γ ⊢ b : B".to_string()],
278            conclusion: "Γ ⊢ inr b : A + B".to_string(),
279            side_conditions: vec![],
280        });
281
282        ts.typing_rules.push(TypingRule {
283            name: "case-elim".to_string(),
284            premises: vec![
285                "Γ ⊢ e : A + B".to_string(),
286                "Γ, x:A ⊢ e₁ : C".to_string(),
287                "Γ, y:B ⊢ e₂ : C".to_string(),
288            ],
289            conclusion: "Γ ⊢ case e of inl x ⇒ e₁ | inr y ⇒ e₂ : C".to_string(),
290            side_conditions: vec![],
291        });
292
293        // β-rules
294        ts.reduction_rules.push(ReductionRule {
295            name: "case-inl-beta".to_string(),
296            lhs: "case (inl a) of inl x ⇒ e₁ | inr y ⇒ e₂".to_string(),
297            rhs: "e₁[a/x]".to_string(),
298        });
299        ts.reduction_rules.push(ReductionRule {
300            name: "case-inr-beta".to_string(),
301            lhs: "case (inr b) of inl x ⇒ e₁ | inr y ⇒ e₂".to_string(),
302            rhs: "e₂[b/y]".to_string(),
303        });
304
305        // η-rule
306        ts.equivalence_rules.push(EquivalenceRule {
307            name: "sum-eta".to_string(),
308            lhs: "e".to_string(),
309            rhs: "case e of inl x ⇒ inl x | inr y ⇒ inr y".to_string(),
310            condition: Some("e : A + B".to_string()),
311        });
312    }
313
314    fn derive_exponentials(ts: &mut TypeSystem) {
315        // Type: A → B
316        ts.type_constructors.push(TypeConstructor {
317            name: "Arrow".to_string(),
318            symbol: "→".to_string(),
319            arity: 2,
320            kind: TypeConstructorKind::Exponential,
321        });
322
323        // Terms: λx.e, f a
324        ts.term_constructors.push(TermConstructor {
325            name: "abs".to_string(),
326            symbol: "λ_._ ".to_string(),
327            kind: TermConstructorKind::Abstraction,
328        });
329        ts.term_constructors.push(TermConstructor {
330            name: "app".to_string(),
331            symbol: "_ _".to_string(),
332            kind: TermConstructorKind::Application,
333        });
334
335        // Typing rules
336        ts.typing_rules.push(TypingRule {
337            name: "abs-intro".to_string(),
338            premises: vec!["Γ, x:A ⊢ e : B".to_string()],
339            conclusion: "Γ ⊢ λx.e : A → B".to_string(),
340            side_conditions: vec![],
341        });
342
343        ts.typing_rules.push(TypingRule {
344            name: "app-elim".to_string(),
345            premises: vec!["Γ ⊢ f : A → B".to_string(), "Γ ⊢ a : A".to_string()],
346            conclusion: "Γ ⊢ f a : B".to_string(),
347            side_conditions: vec![],
348        });
349
350        // β-rule
351        ts.reduction_rules.push(ReductionRule {
352            name: "beta".to_string(),
353            lhs: "(λx.e) a".to_string(),
354            rhs: "e[a/x]".to_string(),
355        });
356
357        // η-rule
358        ts.equivalence_rules.push(EquivalenceRule {
359            name: "eta".to_string(),
360            lhs: "f".to_string(),
361            rhs: "λx.f x".to_string(),
362            condition: Some("f : A → B, x fresh".to_string()),
363        });
364    }
365
366    fn derive_tensor(ts: &mut TypeSystem, spec: &CategorySpec) {
367        let tensor_spec = spec.structure.tensor.as_ref().unwrap();
368
369        // Type: A ⊗ B
370        ts.type_constructors.push(TypeConstructor {
371            name: "Tensor".to_string(),
372            symbol: tensor_spec.symbol.clone(),
373            arity: 2,
374            kind: TypeConstructorKind::Tensor,
375        });
376
377        // Unit: I
378        ts.type_constructors.push(TypeConstructor {
379            name: "TensorUnit".to_string(),
380            symbol: tensor_spec.unit_symbol.clone(),
381            arity: 0,
382            kind: TypeConstructorKind::Unit,
383        });
384
385        // Terms
386        ts.term_constructors.push(TermConstructor {
387            name: "tensor-pair".to_string(),
388            symbol: format!("(_ {} _)", tensor_spec.symbol),
389            kind: TermConstructorKind::PairIntro,
390        });
391        ts.term_constructors.push(TermConstructor {
392            name: "let-tensor".to_string(),
393            symbol: "let (x ⊗ y) = _ in _".to_string(),
394            kind: TermConstructorKind::LetPair,
395        });
396
397        // Linear pair introduction (context splitting)
398        ts.typing_rules.push(TypingRule {
399            name: "tensor-intro".to_string(),
400            premises: vec!["Γ₁ ⊢ a : A".to_string(), "Γ₂ ⊢ b : B".to_string()],
401            conclusion: format!("Γ₁, Γ₂ ⊢ (a {} b) : A {} B", tensor_spec.symbol, tensor_spec.symbol),
402            side_conditions: vec!["Γ₁ ∩ Γ₂ = ∅".to_string()],
403        });
404
405        // Linear pair elimination
406        ts.typing_rules.push(TypingRule {
407            name: "tensor-elim".to_string(),
408            premises: vec![
409                format!("Γ₁ ⊢ p : A {} B", tensor_spec.symbol),
410                "Γ₂, x:A, y:B ⊢ e : C".to_string(),
411            ],
412            conclusion: "Γ₁, Γ₂ ⊢ let (x ⊗ y) = p in e : C".to_string(),
413            side_conditions: vec![],
414        });
415
416        // β-rule
417        ts.reduction_rules.push(ReductionRule {
418            name: "tensor-beta".to_string(),
419            lhs: "let (x ⊗ y) = (a ⊗ b) in e".to_string(),
420            rhs: "e[a/x, b/y]".to_string(),
421        });
422    }
423
424    fn derive_linear_hom(ts: &mut TypeSystem) {
425        // Type: A ⊸ B
426        ts.type_constructors.push(TypeConstructor {
427            name: "Lollipop".to_string(),
428            symbol: "⊸".to_string(),
429            arity: 2,
430            kind: TypeConstructorKind::LinearHom,
431        });
432
433        // Terms: λx.e (linear), f a
434        ts.term_constructors.push(TermConstructor {
435            name: "linear-abs".to_string(),
436            symbol: "λ°_._ ".to_string(),
437            kind: TermConstructorKind::Abstraction,
438        });
439
440        // Typing rules
441        ts.typing_rules.push(TypingRule {
442            name: "linear-abs-intro".to_string(),
443            premises: vec!["Γ, x:A ⊢ e : B".to_string()],
444            conclusion: "Γ ⊢ λx.e : A ⊸ B".to_string(),
445            side_conditions: vec!["x appears exactly once in e".to_string()],
446        });
447
448        ts.typing_rules.push(TypingRule {
449            name: "linear-app-elim".to_string(),
450            premises: vec!["Γ₁ ⊢ f : A ⊸ B".to_string(), "Γ₂ ⊢ a : A".to_string()],
451            conclusion: "Γ₁, Γ₂ ⊢ f a : B".to_string(),
452            side_conditions: vec!["Γ₁ ∩ Γ₂ = ∅".to_string()],
453        });
454
455        // β-rule
456        ts.reduction_rules.push(ReductionRule {
457            name: "linear-beta".to_string(),
458            lhs: "(λx.e) a".to_string(),
459            rhs: "e[a/x]".to_string(),
460        });
461
462        // η-rule
463        ts.equivalence_rules.push(EquivalenceRule {
464            name: "linear-eta".to_string(),
465            lhs: "f".to_string(),
466            rhs: "λx.f x".to_string(),
467            condition: Some("f : A ⊸ B, x fresh".to_string()),
468        });
469    }
470}
471
472#[cfg(test)]
473mod tests {
474    use super::*;
475    use csw_core::CategoryBuilder;
476
477    #[test]
478    fn test_derive_stlc() {
479        let ccc = CategoryBuilder::new("STLC")
480            .with_base("Int")
481            .with_terminal()
482            .with_products()
483            .with_exponentials()
484            .cartesian()
485            .build()
486            .unwrap();
487
488        let ts = Deriver::derive(&ccc);
489
490        assert_eq!(ts.name, "STLC");
491        assert!(ts.structural.weakening);
492        assert!(ts.structural.contraction);
493        assert!(ts.structural.exchange);
494
495        // Check type constructors
496        let type_names: Vec<_> = ts.type_constructors.iter().map(|t| &t.name).collect();
497        assert!(type_names.contains(&&"Int".to_string()));
498        assert!(type_names.contains(&&"Unit".to_string()));
499        assert!(type_names.contains(&&"Product".to_string()));
500        assert!(type_names.contains(&&"Arrow".to_string()));
501    }
502
503    #[test]
504    fn test_derive_linear() {
505        let smcc = CategoryBuilder::new("Linear")
506            .with_base("Int")
507            .with_tensor()
508            .with_linear_hom()
509            .with_symmetry()
510            .linear()
511            .build()
512            .unwrap();
513
514        let ts = Deriver::derive(&smcc);
515
516        assert_eq!(ts.name, "Linear");
517        assert!(!ts.structural.weakening);
518        assert!(!ts.structural.contraction);
519        assert!(ts.structural.exchange);
520
521        // Check type constructors
522        let type_names: Vec<_> = ts.type_constructors.iter().map(|t| &t.name).collect();
523        assert!(type_names.contains(&&"Tensor".to_string()));
524        assert!(type_names.contains(&&"Lollipop".to_string()));
525    }
526
527    #[test]
528    fn test_print_type_system() {
529        let ccc = CategoryBuilder::new("STLC")
530            .with_terminal()
531            .with_products()
532            .with_exponentials()
533            .cartesian()
534            .build()
535            .unwrap();
536
537        let ts = Deriver::derive(&ccc);
538        let output = ts.to_string();
539
540        assert!(output.contains("STLC Type System"));
541        assert!(output.contains("Weakening:   ✓"));
542    }
543}