catlog/dbl/
theory.rs

1//! Double theories.
2//!
3//! A double theory specifies a categorical structure, meaning a category (or
4//! categories) equipped with extra structure. The spirit of the formalism is that a
5//! double theory is "just" a [virtual double category](super::category),
6//! categorifying Lawvere's idea that a theory is "just" a category. Thus, a double
7//! theory is a [concept with an
8//! attitude](https://ncatlab.org/nlab/show/concept+with+an+attitude). To bring out
9//! these intuitions, the interface for double theories, [`DblTheory`], introduces
10//! new terminology compared to the references cited below.
11//!
12//! # Terminology
13//!
14//! A double theory comprises four kinds of things:
15//!
16//! 1. **Object type**, interpreted in models as a set of objects.
17//!
18//! 2. **Morphism type**, having a source and a target object type and interpreted
19//!    in models as a span of morphisms (or
20//!    [heteromorphisms](https://ncatlab.org/nlab/show/heteromorphism)) between sets
21//!    of objects.
22//!
23//! 3. **Object operation**, interpreted in models as a function between sets of
24//!    objects.
25//!
26//! 4. **Morphism operation**, having a source and target object operation and
27//!    interpreted in models as map between spans of morphisms.
28//!
29//! The dictionary between the type-theoretic and double-categorical terminology is
30//! summarized by the table:
31//!
32//! | Associated type                 | Double theory      | Double category           | Interpreted as |
33//! |---------------------------------|--------------------|---------------------------|----------------|
34//! | [`ObType`](DblTheory::ObType)   | Object type        | Object                    | Set            |
35//! | [`MorType`](DblTheory::MorType) | Morphism type      | Proarrow (loose morphism) | Span           |
36//! | [`ObOp`](DblTheory::ObOp)       | Object operation   | Arrow (tight morphism)    | Function       |
37//! | [`MorOp`](DblTheory::MorOp)     | Morphism operation | Cell                      | Map of spans   |
38//!
39//! Models of a double theory are *categorical* structures, rather than merely
40//! set-theoretical* ones, because each object type is assigned not just a set of
41//! objects but also a span of morphisms between those objects, constituting a
42//! category. The morphisms come from a distinguished "Hom" morphism type for each
43//! object type in the double theory. Similarly, each object operation is not just a
44//! function but a functor because it comes with an "Hom" operation between the Hom
45//! types. Moreover, morphism types can be composed to give new ones, as summarized
46//! by the table:
47//!
48//! | Method                                      | Double theory          | Double category        |
49//! |---------------------------------------------|------------------------|------------------------|
50//! | [`hom_type`](DblTheory::hom_type)           | Hom type               | Identity proarrow      |
51//! | [`hom_op`](DblTheory::hom_op)               | Hom operation          | Identity cell on arrow |
52//! | [`compose_types`](DblTheory::compose_types) | Compose morphism types | Compose proarrows      |
53//!
54//! Finally, operations on both objects and morphisms have identities and can be
55//! composed:
56//!
57//! | Method                                          | Double theory                       | Double category           |
58//! |-------------------------------------------------|-------------------------------------|---------------------------|
59//! | [`id_ob_op`](DblTheory::id_ob_op)               | Identity operation on object type   | Identity arrow            |
60//! | [`id_mor_op`](DblTheory::id_mor_op)             | Identity operation on morphism type | Identity cell on proarrow |
61//! | [`compose_ob_ops`](DblTheory::compose_ob_ops)   | Compose object operations           | Compose arrows            |
62//! | [`compose_mor_ops`](DblTheory::compose_mor_ops) | Compose morphism operations         | Compose cells             |
63//!
64//! # References
65//!
66//! - [Lambert & Patterson, 2024](crate::refs::CartDblTheories)
67//! - [Patterson, 2024](crate::refs::DblProducts),
68//!   Section 10: Finite-product double theories
69
70use nonempty::NonEmpty;
71
72use super::{category::*, graph::InvalidVDblGraph, tree::*};
73use crate::one::{InvalidPathEq, Path, tree::OpenTree};
74use crate::zero::QualifiedName;
75
76pub use super::discrete::theory::*;
77pub use super::discrete_tabulator::theory::*;
78pub use super::modal::theory::*;
79
80/// A double theory.
81///
82/// A double theory is "just" a virtual double category (VDC) assumed to have units.
83/// Reflecting this, this trait has a blanket implementation for any
84/// [`VDblCategory`]. It is not recommended to implement this trait directly.
85///
86/// See the [module-level docs](super::theory) for background on the terminology.
87pub trait DblTheory {
88    /// Rust type of object types in the theory.
89    ///
90    /// Viewing the double theory as a virtual double category, this is the type of
91    /// objects.
92    type ObType: Eq + Clone;
93
94    /// Rust type of morphism types in the theory.
95    ///
96    /// Viewing the double theory as a virtual double category, this is the type of
97    /// proarrows.
98    type MorType: Eq + Clone;
99
100    /// Rust type of operations on objects in the double theory.
101    ///
102    /// Viewing the double theory as a virtual double category, this is the type of
103    /// arrows.
104    type ObOp: Eq + Clone;
105
106    /// Rust type of operations on morphisms in the double theory.
107    ///
108    /// Viewing the double theory as a virtual double category, this is the type of
109    /// cells.
110    type MorOp: Eq + Clone;
111
112    /// Does the object type belong to the theory?
113    fn has_ob_type(&self, x: &Self::ObType) -> bool;
114
115    /// Does the morphism type belong to the theory?
116    fn has_mor_type(&self, m: &Self::MorType) -> bool;
117
118    /// Does the object operation belong to the theory?
119    fn has_ob_op(&self, f: &Self::ObOp) -> bool;
120
121    /// Does the morphism operation belong to the theory?
122    fn has_mor_op(&self, α: &Self::MorOp) -> bool;
123
124    /// Source of a morphism type.
125    fn src_type(&self, m: &Self::MorType) -> Self::ObType;
126
127    /// Target of a morphism type.
128    fn tgt_type(&self, m: &Self::MorType) -> Self::ObType;
129
130    /// Domain of an operation on objects.
131    fn ob_op_dom(&self, f: &Self::ObOp) -> Self::ObType;
132
133    /// Codomain of an operation on objects.
134    fn ob_op_cod(&self, f: &Self::ObOp) -> Self::ObType;
135
136    /// Source operation of an operation on morphisms.
137    fn src_op(&self, α: &Self::MorOp) -> Self::ObOp;
138
139    /// Target operation of an operation on morphisms.
140    fn tgt_op(&self, α: &Self::MorOp) -> Self::ObOp;
141
142    /// Domain of an operation on morphisms, a path of morphism types.
143    fn mor_op_dom(&self, α: &Self::MorOp) -> Path<Self::ObType, Self::MorType>;
144
145    /// Codomain of an operation on morphisms, a single morphism type.
146    fn mor_op_cod(&self, α: &Self::MorOp) -> Self::MorType;
147
148    /// Composes a sequence of morphism types, if they have a composite.
149    fn compose_types(&self, path: Path<Self::ObType, Self::MorType>) -> Option<Self::MorType>;
150
151    /// Hom morphism type on an object type.
152    ///
153    /// Viewing the double theory as a virtual double category, this is the unit
154    /// proarrow on an object.
155    fn hom_type(&self, x: Self::ObType) -> Self::MorType {
156        self.compose_types(Path::Id(x))
157            .expect("A double theory should have a hom type for each object type")
158    }
159
160    /// Compose a sequence of operations on objects.
161    fn compose_ob_ops(&self, path: Path<Self::ObType, Self::ObOp>) -> Self::ObOp;
162
163    /// Identity operation on an object type.
164    ///
165    /// View the double theory as a virtual double category, this is the identity
166    /// arrow on an object.
167    fn id_ob_op(&self, x: Self::ObType) -> Self::ObOp {
168        self.compose_ob_ops(Path::Id(x))
169    }
170
171    /// Hom morphism operation on an object operation.
172    ///
173    /// Viewing the double theory as a virtual double category, this is the unit
174    /// cell on an arrow.
175    fn hom_op(&self, f: Self::ObOp) -> Self::MorOp;
176
177    /// Compose operations on morphisms.
178    fn compose_mor_ops(&self, tree: DblTree<Self::ObOp, Self::MorType, Self::MorOp>)
179    -> Self::MorOp;
180
181    /// Identity operation on a morphism type.
182    ///
183    /// Viewing the double theory as a virtual double category, this is the identity
184    /// cell on a proarrow.
185    fn id_mor_op(&self, m: Self::MorType) -> Self::MorOp {
186        self.compose_mor_ops(DblTree::empty(m))
187    }
188}
189
190impl<VDC: VDCWithComposites> DblTheory for VDC {
191    type ObType = VDC::Ob;
192    type MorType = VDC::Pro;
193    type ObOp = VDC::Arr;
194    type MorOp = VDC::Cell;
195
196    fn has_ob_type(&self, x: &Self::ObType) -> bool {
197        self.has_ob(x)
198    }
199    fn has_mor_type(&self, m: &Self::MorType) -> bool {
200        self.has_proarrow(m)
201    }
202    fn has_ob_op(&self, f: &Self::ObOp) -> bool {
203        self.has_arrow(f)
204    }
205    fn has_mor_op(&self, α: &Self::MorOp) -> bool {
206        self.has_cell(α)
207    }
208
209    fn src_type(&self, m: &Self::MorType) -> Self::ObType {
210        self.src(m)
211    }
212    fn tgt_type(&self, m: &Self::MorType) -> Self::ObType {
213        self.tgt(m)
214    }
215    fn ob_op_dom(&self, f: &Self::ObOp) -> Self::ObType {
216        self.dom(f)
217    }
218    fn ob_op_cod(&self, f: &Self::ObOp) -> Self::ObType {
219        self.cod(f)
220    }
221
222    fn src_op(&self, α: &Self::MorOp) -> Self::ObOp {
223        self.cell_src(α)
224    }
225    fn tgt_op(&self, α: &Self::MorOp) -> Self::ObOp {
226        self.cell_tgt(α)
227    }
228    fn mor_op_dom(&self, α: &Self::MorOp) -> Path<Self::ObType, Self::MorType> {
229        self.cell_dom(α)
230    }
231    fn mor_op_cod(&self, α: &Self::MorOp) -> Self::MorType {
232        self.cell_cod(α)
233    }
234
235    fn compose_types(&self, path: Path<Self::ObType, Self::MorType>) -> Option<Self::MorType> {
236        self.composite(path)
237    }
238    fn hom_type(&self, x: Self::ObType) -> Self::MorType {
239        self.unit(x).expect("A double theory should have all hom types")
240    }
241    fn hom_op(&self, f: Self::ObOp) -> Self::MorOp {
242        let y = self.cod(&f);
243        let y_ext = self.unit_ext(y).expect("Codomain of arrow should have hom type");
244        let cell = self.compose_cells(DblTree(
245            OpenTree::linear(vec![DblNode::Spine(f), DblNode::Cell(y_ext)]).unwrap(),
246        ));
247        self.through_unit(cell, 0).expect("Domain of arrow should have hom type")
248    }
249
250    fn compose_ob_ops(&self, path: Path<Self::ObType, Self::ObOp>) -> Self::ObOp {
251        self.compose(path)
252    }
253    fn id_ob_op(&self, x: Self::ObType) -> Self::ObOp {
254        self.id(x)
255    }
256
257    fn compose_mor_ops(
258        &self,
259        tree: DblTree<Self::ObOp, Self::MorType, Self::MorOp>,
260    ) -> Self::MorOp {
261        self.compose_cells(tree)
262    }
263    fn id_mor_op(&self, m: Self::MorType) -> Self::MorOp {
264        self.id_cell(m)
265    }
266}
267
268/// A failure of a double theory to be well defined.
269#[derive(Debug)]
270pub enum InvalidDblTheory {
271    /// Morphism type with an invalid source type.
272    SrcType(QualifiedName),
273
274    /// Morphism type with an invalid target type.
275    TgtType(QualifiedName),
276
277    /// Object operation with an invalid domain.
278    ObOpDom(QualifiedName),
279
280    /// Object operation with an invalid codomain.
281    ObOpCod(QualifiedName),
282
283    /// Morphism operation with an invalid domain.
284    MorOpDom(QualifiedName),
285
286    /// Morphism operation with an invalid codomain.
287    MorOpCod(QualifiedName),
288
289    /// Morphism operation with an invalid source operation.
290    SrcOp(QualifiedName),
291
292    /// Morphism operation with an invalid target operation.
293    TgtOp(QualifiedName),
294
295    /// Morphism operation having a boundary with incompatible corners.
296    MorOpBoundary(QualifiedName),
297
298    /// Equation between morphism types with one or more errors.
299    MorTypeEq(usize, NonEmpty<InvalidPathEq>),
300
301    /// Equation between object operations with one or more errors.
302    ObOpEq(usize, NonEmpty<InvalidPathEq>),
303}
304
305impl From<InvalidVDblGraph<QualifiedName, QualifiedName, QualifiedName>> for InvalidDblTheory {
306    fn from(err: InvalidVDblGraph<QualifiedName, QualifiedName, QualifiedName>) -> Self {
307        match err {
308            InvalidVDblGraph::Dom(id) => InvalidDblTheory::ObOpDom(id),
309            InvalidVDblGraph::Cod(id) => InvalidDblTheory::ObOpCod(id),
310            InvalidVDblGraph::Src(id) => InvalidDblTheory::SrcType(id),
311            InvalidVDblGraph::Tgt(id) => InvalidDblTheory::TgtType(id),
312            InvalidVDblGraph::SquareDom(id) => InvalidDblTheory::MorOpDom(id),
313            InvalidVDblGraph::SquareCod(id) => InvalidDblTheory::MorOpCod(id),
314            InvalidVDblGraph::SquareSrc(id) => InvalidDblTheory::SrcOp(id),
315            InvalidVDblGraph::SquareTgt(id) => InvalidDblTheory::TgtOp(id),
316            InvalidVDblGraph::NotSquare(id) => InvalidDblTheory::MorOpBoundary(id),
317        }
318    }
319}