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 catlog::dbl::theory::{
13    self, DblTheory as _, ModalMorType, ModalObOp, ModalObType, ModeApp, TabMorType, TabObOp,
14    TabObType,
15};
16use catlog::one::{Path, QualifiedPath, ShortPath};
17use catlog::tt::{
18    self,
19    notebook_elab::{demote_modality, promote_modality},
20};
21use catlog::zero::{NameSegment, QualifiedName};
22use notebook_types::current::theory::*;
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 double theory.
234    Modal(Rc<theory::ModalDblTheory>),
235}
236
237/// Wasm binding for a double theory.
238#[wasm_bindgen]
239pub struct DblTheory(#[wasm_bindgen(skip)] pub DblTheoryBox);
240
241impl DblTheory {
242    /// Tries to get a discrete double theory.
243    pub fn discrete(&self) -> Result<&Rc<theory::DiscreteDblTheory>, String> {
244        (&self.0).try_into().map_err(|_| "Theory should be discrete".into())
245    }
246
247    /// Tries to convert into a theory usable by DoubleTT.
248    pub fn try_into_tt(&self) -> Option<tt::theory::TheoryDef> {
249        match &self.0 {
250            DblTheoryBox::Discrete(th) => Some(tt::theory::TheoryDef::Discrete(th.clone())),
251            DblTheoryBox::Modal(th) => Some(tt::theory::TheoryDef::Modal(th.clone())),
252            _ => None,
253        }
254    }
255}
256
257#[wasm_bindgen]
258impl DblTheory {
259    /// Returns whether the theory contains the object type.
260    #[wasm_bindgen(js_name = "hasObType")]
261    pub fn has_ob_type(&self, ob_type: ObType) -> Result<bool, String> {
262        all_the_same!(match &self.0 {
263            DblTheoryBox::[Discrete, DiscreteTab, Modal](th) => {
264                Ok(th.has_ob_type(&Elaborator.elab(&ob_type)?))
265            }
266        })
267    }
268
269    /// Returns whether the theory contains the morphism type.
270    #[wasm_bindgen(js_name = "hasMorType")]
271    pub fn has_mor_type(&self, mor_type: MorType) -> Result<bool, String> {
272        all_the_same!(match &self.0 {
273            DblTheoryBox::[Discrete, DiscreteTab, Modal](th) => {
274                Ok(th.has_mor_type(&Elaborator.elab(&mor_type)?))
275            }
276        })
277    }
278
279    /// Gets the source of a morphism type.
280    #[wasm_bindgen]
281    pub fn src(&self, mor_type: MorType) -> Result<ObType, String> {
282        all_the_same!(match &self.0 {
283            DblTheoryBox::[Discrete, DiscreteTab, Modal](th) => {
284                let mor_type = Elaborator.elab(&mor_type)?;
285                Ok(Quoter.quote(&th.src_type(&mor_type)))
286            }
287        })
288    }
289
290    /// Gets the target of a morphism type.
291    #[wasm_bindgen]
292    pub fn tgt(&self, mor_type: MorType) -> Result<ObType, String> {
293        all_the_same!(match &self.0 {
294            DblTheoryBox::[Discrete, DiscreteTab, Modal](th) => {
295                let mor_type = Elaborator.elab(&mor_type)?;
296                Ok(Quoter.quote(&th.tgt_type(&mor_type)))
297            }
298        })
299    }
300
301    /// Gets the domain of an object operation.
302    #[wasm_bindgen]
303    pub fn dom(&self, op: ObOp) -> Result<ObType, String> {
304        all_the_same!(match &self.0 {
305            DblTheoryBox::[Discrete, DiscreteTab, Modal](th) => {
306                let op = Elaborator.elab(&op)?;
307                Ok(Quoter.quote(&th.ob_op_dom(&op)))
308            }
309        })
310    }
311
312    /// Gets the codomain of an object operation.
313    #[wasm_bindgen]
314    pub fn cod(&self, op: ObOp) -> Result<ObType, String> {
315        all_the_same!(match &self.0 {
316            DblTheoryBox::[Discrete, DiscreteTab, Modal](th) => {
317                let op = Elaborator.elab(&op)?;
318                Ok(Quoter.quote(&th.ob_op_cod(&op)))
319            }
320        })
321    }
322
323    /// Does the theory support instantiating models in other models?
324    ///
325    /// Eventually instantiation should be supported for every double doctrine
326    /// and then this method can be removed.
327    #[wasm_bindgen(js_name = "canInstantiateModels")]
328    pub fn can_instantiate_models(&self) -> bool {
329        !matches!(&self.0, DblTheoryBox::DiscreteTab(_))
330    }
331}
332
333/// Mapping from object types to numerical indices.
334///
335/// Like [`MorTypeIndex`], this struct just compensates for the lack of hash maps
336/// with arbitrary keys in JavaScript.
337#[wasm_bindgen]
338#[derive(Clone, Default)]
339pub struct ObTypeIndex(HashMap<ObType, usize>);
340
341#[wasm_bindgen]
342impl ObTypeIndex {
343    /// Creates a new object type index.
344    #[wasm_bindgen(constructor)]
345    pub fn new() -> Self {
346        Default::default()
347    }
348
349    /// Returns whether the object type is set.
350    #[wasm_bindgen(js_name = "has")]
351    pub fn contains_key(&self, x: &ObType) -> bool {
352        self.0.contains_key(x)
353    }
354
355    /// Gets the index of an object type, if set.
356    #[wasm_bindgen]
357    pub fn get(&self, x: &ObType) -> Option<usize> {
358        self.0.get(x).copied()
359    }
360
361    /// Sets the index of an object type.
362    #[wasm_bindgen(js_name = "set")]
363    pub fn insert(&mut self, x: ObType, i: usize) {
364        self.0.insert(x, i);
365    }
366}
367
368/// Mapping from morphism types to numerical indices.
369///
370/// Like [`ObTypeIndex`], this struct just compensates for the lack of hash maps
371/// with arbitrary keys in JavaScript.
372#[wasm_bindgen]
373#[derive(Clone, Default)]
374pub struct MorTypeIndex(HashMap<MorType, usize>);
375
376#[wasm_bindgen]
377impl MorTypeIndex {
378    /// Creates a new morphism type index.
379    #[wasm_bindgen(constructor)]
380    pub fn new() -> Self {
381        Default::default()
382    }
383
384    /// Returns whether the morphism type is set.
385    #[wasm_bindgen(js_name = "has")]
386    pub fn contains_key(&self, m: &MorType) -> bool {
387        self.0.contains_key(m)
388    }
389
390    /// Gets the index of a morphism type, if set.
391    #[wasm_bindgen]
392    pub fn get(&self, m: &MorType) -> Option<usize> {
393        self.0.get(m).copied()
394    }
395
396    /// Sets the index of a morphism type.
397    #[wasm_bindgen(js_name = "set")]
398    pub fn insert(&mut self, m: MorType, i: usize) {
399        self.0.insert(m, i);
400    }
401}