catlog_wasm/
theory.rs

1//! Wasm bindings for double theories.
2
3use std::collections::HashMap;
4use std::rc::Rc;
5
6use all_the_same::all_the_same;
7use derive_more::{From, TryInto};
8use ustr::Ustr;
9
10use wasm_bindgen::prelude::*;
11
12use catcolab_document_types::current::theory::*;
13use catlog::dbl::theory::{
14    self, DblTheory as _, ModalMorType, ModalObOp, ModalObType, ModeApp, NonUnital, TabMorType,
15    TabObOp, TabObType, Unital,
16};
17use catlog::one::{Path, QualifiedPath, ShortPath};
18use catlog::tt::{
19    self,
20    notebook_elab::{demote_modality, promote_modality},
21};
22use catlog::zero::{NameSegment, QualifiedName};
23
24use super::notation::*;
25
26/// Elaborates into object type in a discrete double theory.
27impl CanElaborate<ObType, QualifiedName> for Elaborator {
28    fn elab(&self, ob_type: &ObType) -> Result<QualifiedName, String> {
29        match ob_type {
30            ObType::Basic(id) => Ok((*id).into()),
31            _ => Err(format!("Cannot use object type in discrete double theory: {ob_type:#?}")),
32        }
33    }
34}
35
36/// Elaborates into morphism type in a discrete double theory.
37impl CanElaborate<MorType, QualifiedPath> for Elaborator {
38    fn elab(&self, mor_type: &MorType) -> Result<QualifiedPath, String> {
39        match mor_type {
40            MorType::Basic(id) => Ok(Path::single((*id).into())),
41            MorType::Composite(fs) => {
42                let fs: Result<Vec<_>, _> = fs.iter().map(|f| self.elab(f)).collect();
43                let path = Path::from_vec(fs?).ok_or("Composite should not be empty")?;
44                Ok(path.flatten())
45            }
46            MorType::Hom(ob_type) => Ok(Path::Id(self.elab(ob_type.as_ref())?)),
47            _ => Err(format!("Cannot use morphsim type in discrete double theory: {mor_type:#?}")),
48        }
49    }
50}
51
52/// Elaborates into object operation in a discrete double theory.
53impl CanElaborate<ObOp, QualifiedName> for Elaborator {
54    fn elab(&self, op: &ObOp) -> Result<QualifiedName, String> {
55        Err(format!("Cannot use operation in discrete double theory: {op:#?}"))
56    }
57}
58
59/// Elaborates into object type in a discrete tabulator theory.
60impl CanElaborate<ObType, TabObType> for Elaborator {
61    fn elab(&self, ob_type: &ObType) -> Result<TabObType, String> {
62        match ob_type {
63            ObType::Basic(id) => Ok(TabObType::Basic((*id).into())),
64            ObType::Tabulator(mor_type) => {
65                Ok(TabObType::Tabulator(Box::new(self.elab(mor_type.as_ref())?)))
66            }
67            _ => Err(format!("Cannot use object type in discrete tabulator theory: {ob_type:#?}")),
68        }
69    }
70}
71
72/// Elaborates into morphism type in a discrete tabulator theory.
73impl CanElaborate<MorType, TabMorType> for Elaborator {
74    fn elab(&self, mor_type: &MorType) -> Result<TabMorType, String> {
75        match mor_type {
76            MorType::Basic(id) => Ok(TabMorType::Basic((*id).into())),
77            MorType::Composite(_) => {
78                Err("Composites not yet implemented for tabulator theories".into())
79            }
80            MorType::Hom(ob_type) => Ok(TabMorType::Hom(Box::new(self.elab(ob_type.as_ref())?))),
81            _ => {
82                Err(format!("Cannot use morphism type in discrete tabulator theory: {mor_type:#?}"))
83            }
84        }
85    }
86}
87
88/// Elaborates into object operation in a discrete tabulator theory.
89impl CanElaborate<ObOp, TabObOp> for Elaborator {
90    fn elab(&self, op: &ObOp) -> Result<TabObOp, String> {
91        Err(format!("Cannot use operation in discrete tabulator theory: {op:#?}"))
92    }
93}
94
95/// Elaborates into object type in a modal double theory.
96impl CanElaborate<ObType, ModalObType> for Elaborator {
97    fn elab(&self, ob_type: &ObType) -> Result<ModalObType, String> {
98        match ob_type {
99            ObType::Basic(id) => Ok(ModeApp::new((*id).into())),
100            ObType::ModeApp { modality, ob_type } => Ok({
101                let ob_type: ModalObType = self.elab(ob_type.as_ref())?;
102                ob_type.apply(promote_modality(*modality))
103            }),
104            _ => Err(format!("Cannot use object type in modal theory: {ob_type:#?}")),
105        }
106    }
107}
108
109/// Elaborates into morphism type in a modal double theory.
110impl CanElaborate<MorType, ModalMorType> for Elaborator {
111    fn elab(&self, mor_type: &MorType) -> Result<ModalMorType, String> {
112        match mor_type {
113            MorType::Basic(id) => Ok(ModeApp::new((*id).into()).into()),
114            MorType::Hom(ob_type) => Ok(ShortPath::Zero(self.elab(ob_type.as_ref())?)),
115            MorType::ModeApp { modality, mor_type } => Ok({
116                let mor_type: ModalMorType = self.elab(mor_type.as_ref())?;
117                mor_type.apply(promote_modality(*modality))
118            }),
119            _ => Err(format!("Cannot use morphism type in modal theory: {mor_type:#?}")),
120        }
121    }
122}
123
124/// Elaborates into an object operation in a modal double theory.
125impl CanElaborate<ObOp, ModalObOp> for Elaborator {
126    fn elab(&self, op: &ObOp) -> Result<ModalObOp, String> {
127        match op {
128            ObOp::Basic(id) => Ok(ModalObOp::generator((*id).into())),
129        }
130    }
131}
132
133pub(crate) fn expect_single_name(name: &QualifiedName) -> Ustr {
134    match name.only() {
135        Some(NameSegment::Text(text)) => text,
136        _ => panic!("Only singleton names are currently supported in notebook types"),
137    }
138}
139
140/// Quotes an object type in a discrete double theory.
141impl CanQuote<QualifiedName, ObType> for Quoter {
142    fn quote(&self, name: &QualifiedName) -> ObType {
143        ObType::Basic(expect_single_name(name))
144    }
145}
146
147/// Quotes a morphism type in a discrete double theory.
148impl CanQuote<QualifiedPath, MorType> for Quoter {
149    fn quote(&self, path: &QualifiedPath) -> MorType {
150        match path {
151            Path::Id(v) => MorType::Hom(Box::new(ObType::Basic(expect_single_name(v)))),
152            Path::Seq(edges) => {
153                if edges.len() == 1 {
154                    MorType::Basic(expect_single_name(&edges.head))
155                } else {
156                    MorType::Composite(
157                        edges.iter().map(|e| MorType::Basic(expect_single_name(e))).collect(),
158                    )
159                }
160            }
161        }
162    }
163}
164
165/// Quotes an object type in a discrete tabulator theory.
166impl CanQuote<TabObType, ObType> for Quoter {
167    fn quote(&self, ob_type: &TabObType) -> ObType {
168        match ob_type {
169            TabObType::Basic(name) => ObType::Basic(expect_single_name(name)),
170            TabObType::Tabulator(mor_type) => {
171                ObType::Tabulator(Box::new(self.quote(mor_type.as_ref())))
172            }
173        }
174    }
175}
176
177/// Quotes a morphism type in a discrete tabulator theory.
178impl CanQuote<TabMorType, MorType> for Quoter {
179    fn quote(&self, mor_type: &TabMorType) -> MorType {
180        match mor_type {
181            TabMorType::Basic(name) => MorType::Basic(expect_single_name(name)),
182            TabMorType::Hom(ob_type) => MorType::Hom(Box::new(self.quote(ob_type.as_ref()))),
183        }
184    }
185}
186
187/// Quotes an object type in a modal theory.
188impl CanQuote<ModalObType, ObType> for Quoter {
189    fn quote(&self, app: &ModalObType) -> ObType {
190        let mut quoted = ObType::Basic(expect_single_name(&app.arg));
191        for modality in &app.modalities {
192            quoted = ObType::ModeApp {
193                modality: demote_modality(*modality),
194                ob_type: quoted.into(),
195            }
196        }
197        quoted
198    }
199}
200
201/// Quotes a morphism type in a modal theory.
202impl CanQuote<ModalMorType, MorType> for Quoter {
203    fn quote(&self, mor_type: &ModalMorType) -> MorType {
204        match mor_type {
205            ShortPath::Zero(ob_type) => MorType::Hom(Box::new(self.quote(ob_type))),
206            ShortPath::One(app) => {
207                let mut quoted = MorType::Basic(expect_single_name(&app.arg));
208                for modality in &app.modalities {
209                    quoted = MorType::ModeApp {
210                        modality: demote_modality(*modality),
211                        mor_type: quoted.into(),
212                    }
213                }
214                quoted
215            }
216        }
217    }
218}
219
220/// A box containing a double theory of any kind.
221///
222/// Ideally the Wasm-bound [`DblTheory`] would just have a type parameter for the
223/// underlying double theory, but `wasm-bindgen` does not support
224/// [generics](https://github.com/rustwasm/wasm-bindgen/issues/3309). Instead, we
225/// explicitly enumerate the supported kinds of double theories in this enum.
226#[derive(From, TryInto)]
227#[try_into(ref)]
228pub enum DblTheoryBox {
229    /// A discrete double theory.
230    Discrete(Rc<theory::DiscreteDblTheory>),
231    /// A discrete tabulator theory.
232    DiscreteTab(Rc<theory::DiscreteTabTheory>),
233    /// A modal unital double theory.
234    ModalUnital(Rc<theory::ModalDblTheory<Unital>>),
235    /// A modal non-unital double theory.
236    ModalNonUnital(Rc<theory::ModalDblTheory<NonUnital>>),
237}
238
239/// Wasm binding for a double theory.
240#[wasm_bindgen]
241pub struct DblTheory(#[wasm_bindgen(skip)] pub DblTheoryBox);
242
243impl DblTheory {
244    /// Tries to get a discrete double theory.
245    pub fn discrete(&self) -> Result<&Rc<theory::DiscreteDblTheory>, String> {
246        (&self.0).try_into().map_err(|_| "Theory should be discrete".into())
247    }
248
249    /// Tries to convert into a theory usable by DoubleTT.
250    pub fn try_into_tt(&self) -> Option<tt::theory::TheoryDef> {
251        match &self.0 {
252            DblTheoryBox::Discrete(th) => Some(tt::theory::TheoryDef::Discrete(th.clone())),
253            DblTheoryBox::DiscreteTab(th) => Some(tt::theory::TheoryDef::DiscreteTab(th.clone())),
254            DblTheoryBox::ModalUnital(th) => Some(tt::theory::TheoryDef::ModalUnital(th.clone())),
255            DblTheoryBox::ModalNonUnital(th) => {
256                Some(tt::theory::TheoryDef::ModalNonUnital(th.clone()))
257            }
258        }
259    }
260}
261
262#[wasm_bindgen]
263impl DblTheory {
264    /// Returns whether the theory contains the object type.
265    #[wasm_bindgen(js_name = "hasObType")]
266    pub fn has_ob_type(&self, ob_type: ObType) -> Result<bool, String> {
267        all_the_same!(match &self.0 {
268            DblTheoryBox::[Discrete, DiscreteTab, ModalUnital, ModalNonUnital](th) => {
269                Ok(th.has_ob_type(&Elaborator.elab(&ob_type)?))
270            }
271        })
272    }
273
274    /// Returns whether the theory contains the morphism type.
275    #[wasm_bindgen(js_name = "hasMorType")]
276    pub fn has_mor_type(&self, mor_type: MorType) -> Result<bool, String> {
277        all_the_same!(match &self.0 {
278            DblTheoryBox::[Discrete, DiscreteTab, ModalUnital, ModalNonUnital](th) => {
279                Ok(th.has_mor_type(&Elaborator.elab(&mor_type)?))
280            }
281        })
282    }
283
284    /// Gets the source of a morphism type.
285    #[wasm_bindgen]
286    pub fn src(&self, mor_type: MorType) -> Result<ObType, String> {
287        all_the_same!(match &self.0 {
288            DblTheoryBox::[Discrete, DiscreteTab, ModalUnital, ModalNonUnital](th) => {
289                let mor_type = Elaborator.elab(&mor_type)?;
290                Ok(Quoter.quote(&th.src_type(&mor_type)))
291            }
292        })
293    }
294
295    /// Gets the target of a morphism type.
296    #[wasm_bindgen]
297    pub fn tgt(&self, mor_type: MorType) -> Result<ObType, String> {
298        all_the_same!(match &self.0 {
299            DblTheoryBox::[Discrete, DiscreteTab, ModalUnital, ModalNonUnital](th) => {
300                let mor_type = Elaborator.elab(&mor_type)?;
301                Ok(Quoter.quote(&th.tgt_type(&mor_type)))
302            }
303        })
304    }
305
306    /// Gets the domain of an object operation.
307    #[wasm_bindgen]
308    pub fn dom(&self, op: ObOp) -> Result<ObType, String> {
309        all_the_same!(match &self.0 {
310            DblTheoryBox::[Discrete, DiscreteTab, ModalUnital, ModalNonUnital](th) => {
311                let op = Elaborator.elab(&op)?;
312                Ok(Quoter.quote(&th.ob_op_dom(&op)))
313            }
314        })
315    }
316
317    /// Gets the codomain of an object operation.
318    #[wasm_bindgen]
319    pub fn cod(&self, op: ObOp) -> Result<ObType, String> {
320        all_the_same!(match &self.0 {
321            DblTheoryBox::[Discrete, DiscreteTab, ModalUnital, ModalNonUnital](th) => {
322                let op = Elaborator.elab(&op)?;
323                Ok(Quoter.quote(&th.ob_op_cod(&op)))
324            }
325        })
326    }
327}
328
329/// Mapping from object types to numerical indices.
330///
331/// Like [`MorTypeIndex`], this struct just compensates for the lack of hash maps
332/// with arbitrary keys in JavaScript.
333#[wasm_bindgen]
334#[derive(Clone, Default)]
335pub struct ObTypeIndex(HashMap<ObType, usize>);
336
337#[wasm_bindgen]
338impl ObTypeIndex {
339    /// Creates a new object type index.
340    #[wasm_bindgen(constructor)]
341    pub fn new() -> Self {
342        Default::default()
343    }
344
345    /// Returns whether the object type is set.
346    #[wasm_bindgen(js_name = "has")]
347    pub fn contains_key(&self, x: &ObType) -> bool {
348        self.0.contains_key(x)
349    }
350
351    /// Gets the index of an object type, if set.
352    #[wasm_bindgen]
353    pub fn get(&self, x: &ObType) -> Option<usize> {
354        self.0.get(x).copied()
355    }
356
357    /// Sets the index of an object type.
358    #[wasm_bindgen(js_name = "set")]
359    pub fn insert(&mut self, x: ObType, i: usize) {
360        self.0.insert(x, i);
361    }
362}
363
364/// Mapping from morphism types to numerical indices.
365///
366/// Like [`ObTypeIndex`], this struct just compensates for the lack of hash maps
367/// with arbitrary keys in JavaScript.
368#[wasm_bindgen]
369#[derive(Clone, Default)]
370pub struct MorTypeIndex(HashMap<MorType, usize>);
371
372#[wasm_bindgen]
373impl MorTypeIndex {
374    /// Creates a new morphism type index.
375    #[wasm_bindgen(constructor)]
376    pub fn new() -> Self {
377        Default::default()
378    }
379
380    /// Returns whether the morphism type is set.
381    #[wasm_bindgen(js_name = "has")]
382    pub fn contains_key(&self, m: &MorType) -> bool {
383        self.0.contains_key(m)
384    }
385
386    /// Gets the index of a morphism type, if set.
387    #[wasm_bindgen]
388    pub fn get(&self, m: &MorType) -> Option<usize> {
389        self.0.get(m).copied()
390    }
391
392    /// Sets the index of a morphism type.
393    #[wasm_bindgen(js_name = "set")]
394    pub fn insert(&mut self, m: MorType, i: usize) {
395        self.0.insert(m, i);
396    }
397}