csw_core/
category.rs

1//! Core categorical specification types.
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6/// A specification of categorical structure.
7///
8/// This is the main type representing a category with its structural features.
9/// From this specification, a type system can be derived.
10#[derive(Clone, Debug, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12pub struct CategorySpec {
13    /// Name of the category/type system
14    pub name: String,
15
16    /// Base objects (primitive types)
17    pub base_objects: Vec<BaseObject>,
18
19    /// Structural features present in the category
20    pub structure: StructuralFeatures,
21}
22
23/// A base/primitive object in the category.
24///
25/// These become the ground types in the derived type system.
26#[derive(Clone, Debug, PartialEq, Eq)]
27#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
28pub struct BaseObject {
29    /// Name of the base type
30    pub name: String,
31
32    /// Optional description
33    pub description: Option<String>,
34}
35
36/// Structural features present in a category.
37///
38/// These determine what type constructors and structural rules
39/// will be present in the derived type system.
40#[derive(Clone, Debug, Default, PartialEq, Eq)]
41#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
42pub struct StructuralFeatures {
43    // === Limits ===
44    /// Terminal object (unit type, 1)
45    pub terminal: bool,
46
47    /// Binary products (pair types, A × B)
48    pub products: bool,
49
50    /// Initial object (empty type, 0)
51    pub initial: bool,
52
53    /// Binary coproducts (sum types, A + B)
54    pub coproducts: bool,
55
56    // === Closed Structure ===
57    /// Exponentials (function types, A → B) - cartesian
58    pub exponentials: bool,
59
60    // === Monoidal Structure ===
61    /// Tensor product specification (A ⊗ B)
62    pub tensor: Option<TensorSpec>,
63
64    /// Linear hom / internal hom (A ⊸ B)
65    pub linear_hom: bool,
66
67    // === Structural Morphisms ===
68    /// Diagonal morphism availability (contraction)
69    pub diagonal: DiagonalSpec,
70
71    /// Terminal morphism availability (weakening)
72    pub terminal_morphism: TerminalSpec,
73
74    /// Symmetry/braiding (exchange rule)
75    pub symmetry: bool,
76}
77
78/// Specification for monoidal tensor product.
79#[derive(Clone, Debug, PartialEq, Eq)]
80#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
81pub struct TensorSpec {
82    /// Symbol for tensor (default: "⊗")
83    pub symbol: String,
84
85    /// Symbol for unit (default: "I")
86    pub unit_symbol: String,
87
88    /// Whether associativity is strict or weak
89    pub associativity: Associativity,
90}
91
92impl Default for TensorSpec {
93    fn default() -> Self {
94        Self {
95            symbol: "⊗".to_string(),
96            unit_symbol: "I".to_string(),
97            associativity: Associativity::Weak,
98        }
99    }
100}
101
102/// Associativity type for monoidal structure.
103#[derive(Clone, Debug, Default, PartialEq, Eq)]
104#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
105pub enum Associativity {
106    /// Strict associativity: (A ⊗ B) ⊗ C = A ⊗ (B ⊗ C)
107    Strict,
108    /// Weak associativity: (A ⊗ B) ⊗ C ≅ A ⊗ (B ⊗ C)
109    #[default]
110    Weak,
111}
112
113/// Specification for diagonal morphism (contraction/copying).
114#[derive(Clone, Debug, Default, PartialEq, Eq)]
115#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
116pub enum DiagonalSpec {
117    /// No diagonal (linear: cannot copy)
118    #[default]
119    None,
120    /// Diagonal for all objects (cartesian: can always copy)
121    Universal,
122    /// Diagonal only for specified objects
123    Restricted(Vec<String>),
124}
125
126/// Specification for terminal morphism (weakening/discarding).
127#[derive(Clone, Debug, Default, PartialEq, Eq)]
128#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
129pub enum TerminalSpec {
130    /// No terminal morphisms (relevant: cannot discard)
131    #[default]
132    None,
133    /// Terminal morphism for all objects (affine/cartesian: can always discard)
134    Universal,
135    /// Only for specified objects
136    Restricted(Vec<String>),
137}
138
139impl CategorySpec {
140    /// Returns true if this category has full structural rules (weakening + contraction + exchange).
141    pub fn is_cartesian(&self) -> bool {
142        matches!(self.structure.diagonal, DiagonalSpec::Universal)
143            && matches!(self.structure.terminal_morphism, TerminalSpec::Universal)
144            && self.structure.symmetry
145    }
146
147    /// Returns true if this category is linear (no weakening, no contraction).
148    pub fn is_linear(&self) -> bool {
149        matches!(self.structure.diagonal, DiagonalSpec::None)
150            && matches!(self.structure.terminal_morphism, TerminalSpec::None)
151    }
152
153    /// Returns true if this category is affine (weakening but no contraction).
154    pub fn is_affine(&self) -> bool {
155        matches!(self.structure.diagonal, DiagonalSpec::None)
156            && matches!(self.structure.terminal_morphism, TerminalSpec::Universal)
157    }
158
159    /// Returns true if this category is relevant (contraction but no weakening).
160    pub fn is_relevant(&self) -> bool {
161        matches!(self.structure.diagonal, DiagonalSpec::Universal)
162            && matches!(self.structure.terminal_morphism, TerminalSpec::None)
163    }
164}