1use std::fmt;
22use std::hash::Hash;
23use std::iter::repeat_n;
24use std::marker::PhantomData;
25
26use derivative::Derivative;
27use derive_more::From;
28use indexmap::IndexMap;
29use ref_cast::RefCast;
30
31use crate::dbl::computad::{AVDCComputad, AVDCComputadTop};
32use crate::dbl::theory::{DblTheoryKind, InvalidDblTheory};
33use crate::dbl::{DblTree, InvalidVDblGraph, VDCWithComposites, VDblCategory, VDblGraph};
34use crate::one::computad::{Computad, ComputadTop};
35use crate::validate::{self, Validate};
36use crate::{one::*, zero::*};
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, From)]
40pub enum Modality {
41 #[from]
43 List(List),
44
45 Discrete(),
47
48 Codiscrete(),
50}
51
52#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
59pub enum List {
60 Plain,
62
63 Symmetric,
65
66 Cocartesian,
72
73 Cartesian,
77
78 Additive,
85}
86
87impl fmt::Display for Modality {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 match self {
90 Modality::List(List::Plain) => write!(f, "List"),
91 Modality::List(list_type) => write!(f, "List.{list_type}"),
92 Modality::Discrete() => write!(f, "Discrete"),
93 Modality::Codiscrete() => write!(f, "Codiscrete"),
94 }
95 }
96}
97
98impl fmt::Display for List {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 fmt::Debug::fmt(self, f)
101 }
102}
103
104#[derive(Clone, Debug, PartialEq, Eq, Hash)]
110pub struct ModeApp<T> {
111 pub arg: T,
113
114 pub modalities: Vec<Modality>,
116}
117
118impl<T> ModeApp<T> {
119 pub fn new(arg: T) -> Self {
121 Self { arg, modalities: Default::default() }
122 }
123
124 pub fn as_ref(&self) -> ModeApp<&T> {
128 ModeApp {
129 arg: &self.arg,
130 modalities: self.modalities.clone(),
131 }
132 }
133
134 pub fn apply(mut self, m: Modality) -> Self {
136 self.modalities.push(m);
137 self
138 }
139
140 pub fn apply_all(mut self, iter: impl IntoIterator<Item = Modality>) -> Self {
142 self.modalities.extend(iter);
143 self
144 }
145
146 pub fn pop_app(mut self) -> (Option<Modality>, Self) {
148 let modality = self.modalities.pop();
149 (modality, self)
150 }
151
152 pub fn map<S, F: FnOnce(T) -> S>(self, f: F) -> ModeApp<S> {
154 let ModeApp { arg, modalities } = self;
155 ModeApp { arg: f(arg), modalities }
156 }
157
158 pub fn flat_map<S, F: FnOnce(T) -> ModeApp<S>>(self, f: F) -> ModeApp<S> {
160 let ModeApp { arg, modalities } = self;
161 f(arg).apply_all(modalities)
162 }
163}
164
165pub type ModalType = ModeApp<QualifiedName>;
169
170#[derive(Clone, Debug, PartialEq, Eq, From)]
175pub enum ModalOp {
176 #[from]
178 Generator(QualifiedName),
179
180 Concat(List, usize, ModalObType),
185}
186
187pub type ModalObType = ModalType;
189
190pub type ModalMorType = ShortPath<ModalType, ModalType>;
192
193impl ModalMorType {
194 pub fn apply(self, m: Modality) -> Self {
196 self.map(|x| x.apply(m), |f| f.apply(m))
197 }
198
199 pub fn apply_all(self, iter: impl IntoIterator<Item = Modality>) -> Self {
201 match self {
202 ShortPath::Zero(x) => ShortPath::Zero(x.apply_all(iter)),
203 ShortPath::One(f) => ShortPath::One(f.apply_all(iter)),
204 }
205 }
206}
207
208pub type ModalObOp = Path<ModalObType, ModeApp<ModalOp>>;
210
211impl ModalObOp {
212 pub fn generator(id: QualifiedName) -> Self {
214 ModeApp::new(ModalOp::Generator(id)).into()
215 }
216
217 pub fn concat(list: List, arity: usize, ob_type: ModalObType) -> Self {
219 ModeApp::new(ModalOp::Concat(list, arity, ob_type)).into()
220 }
221
222 pub fn apply(self, m: Modality) -> Self {
224 self.map(|x| x.apply(m), |f| f.apply(m))
225 }
226
227 pub fn apply_all(self, iter: impl IntoIterator<Item = Modality> + Clone) -> Self {
229 match self {
230 Path::Id(x) => Path::Id(x.apply_all(iter)),
231 Path::Seq(edges) => Path::Seq(edges.map(|p| p.apply_all(iter.clone()))),
232 }
233 }
234}
235
236#[derive(Clone, Debug, PartialEq, Eq, From)]
241pub enum ModalNode {
242 #[from]
244 Basic(ModeApp<ModalOp>),
245
246 Unit(ModeApp<ModalOp>),
248
249 Composite(Path<ModalObType, ModalMorType>),
253}
254
255pub type ModalMorOp = DblTree<ModalObOp, ModalMorType, ModalNode>;
257
258#[derive(Debug, Derivative)]
260#[derivative(Default(new = "true"))]
261pub struct ModalDblTheory<Kind> {
262 _kind: PhantomData<Kind>,
263 ob_generators: HashFinSet<QualifiedName>,
264 arr_generators: ComputadTop<ModalObType, QualifiedName>,
265 pro_generators: ComputadTop<ModalObType, QualifiedName>,
266 cell_generators: AVDCComputadTop<ModalObType, ModalObOp, ModalMorType, QualifiedName>,
267 arr_equations: Vec<PathEq<ModalObType, ModeApp<ModalOp>>>,
268 pro_composites: IndexMap<(ModalType, ModalType), ModalMorType>,
269 }
271
272#[derive(RefCast)]
274#[repr(transparent)]
275pub(super) struct ModalObTypes<Kind>(ModalDblTheory<Kind>);
276
277impl<Kind: DblTheoryKind> Set for ModalObTypes<Kind> {
278 type Elem = ModalObType;
279
280 fn contains(&self, ob: &Self::Elem) -> bool {
281 self.0.ob_generators.contains(&ob.arg)
282 }
283}
284
285#[derive(RefCast)]
287#[repr(transparent)]
288struct ModalProedgeGraph<Kind>(ModalDblTheory<Kind>);
289
290impl<Kind: DblTheoryKind> Graph for ModalProedgeGraph<Kind> {
291 type V = ModalObType;
292 type E = ModalType;
293
294 fn has_vertex(&self, ob: &Self::V) -> bool {
295 self.0.loose_computad().has_vertex(ob)
296 }
297 fn has_edge(&self, proedge: &Self::E) -> bool {
298 self.0.loose_computad().has_edge(&proedge.arg)
299 }
300 fn src(&self, proedge: &Self::E) -> Self::V {
301 proedge.as_ref().flat_map(|e| self.0.loose_computad().src(e))
302 }
303 fn tgt(&self, proedge: &Self::E) -> Self::V {
304 proedge.as_ref().flat_map(|e| self.0.loose_computad().tgt(e))
305 }
306}
307
308#[derive(RefCast)]
310#[repr(transparent)]
311pub(super) struct ModalMorTypeGraph<Kind>(ModalDblTheory<Kind>);
312
313impl<Kind: DblTheoryKind> Graph for ModalMorTypeGraph<Kind> {
314 type V = ModalObType;
315 type E = ModalMorType;
316
317 fn has_vertex(&self, x: &Self::V) -> bool {
318 ModalProedgeGraph::ref_cast(&self.0).has_vertex(x)
319 }
320 fn has_edge(&self, path: &Self::E) -> bool {
321 path.contained_in(ModalProedgeGraph::ref_cast(&self.0))
322 }
323 fn src(&self, path: &Self::E) -> Self::V {
324 path.src(ModalProedgeGraph::ref_cast(&self.0))
325 }
326 fn tgt(&self, path: &Self::E) -> Self::V {
327 path.tgt(ModalProedgeGraph::ref_cast(&self.0))
328 }
329}
330
331impl<Kind: DblTheoryKind> ReflexiveGraph for ModalMorTypeGraph<Kind> {
332 fn refl(&self, x: Self::V) -> Self::E {
333 ShortPath::Zero(x)
334 }
335}
336
337#[derive(RefCast)]
339#[repr(transparent)]
340struct ModalEdgeGraph<Kind>(ModalDblTheory<Kind>);
341
342impl<Kind: DblTheoryKind> Graph for ModalEdgeGraph<Kind> {
343 type V = ModalObType;
344 type E = ModeApp<ModalOp>;
345
346 fn has_vertex(&self, ob: &Self::V) -> bool {
347 self.0.tight_computad().has_vertex(ob)
348 }
349 fn has_edge(&self, edge: &Self::E) -> bool {
350 match &edge.arg {
351 ModalOp::Generator(e) => self.0.tight_computad().has_edge(e),
352 ModalOp::Concat(_, _, ob) => self.0.tight_computad().has_vertex(ob),
353 }
354 }
355 fn src(&self, edge: &Self::E) -> Self::V {
356 edge.as_ref().flat_map(|arg| match arg {
357 ModalOp::Generator(e) => self.0.tight_computad().src(e),
358 ModalOp::Concat(list, n, ob) => {
359 ob.clone().apply_all(repeat_n(Modality::List(*list), *n))
360 }
361 })
362 }
363 fn tgt(&self, edge: &Self::E) -> Self::V {
364 edge.as_ref().flat_map(|arg| match arg {
365 ModalOp::Generator(e) => self.0.tight_computad().tgt(e),
366 ModalOp::Concat(list, _, ob) => ob.clone().apply(Modality::List(*list)),
367 })
368 }
369}
370
371#[derive(RefCast)]
373#[repr(transparent)]
374pub(super) struct ModalOneTheory<Kind>(ModalDblTheory<Kind>);
375
376impl<Kind: DblTheoryKind> Category for ModalOneTheory<Kind> {
377 type Ob = ModalObType;
378 type Mor = ModalObOp;
379
380 fn has_ob(&self, x: &Self::Ob) -> bool {
381 ModalEdgeGraph::ref_cast(&self.0).has_vertex(x)
382 }
383 fn has_mor(&self, path: &Self::Mor) -> bool {
384 path.contained_in(ModalEdgeGraph::ref_cast(&self.0))
385 }
386 fn dom(&self, path: &Self::Mor) -> Self::Ob {
387 path.src(ModalEdgeGraph::ref_cast(&self.0))
388 }
389 fn cod(&self, path: &Self::Mor) -> Self::Ob {
390 path.tgt(ModalEdgeGraph::ref_cast(&self.0))
391 }
392 fn compose(&self, path: Path<Self::Ob, Self::Mor>) -> Self::Mor {
393 path.flatten()
394 }
395}
396
397#[derive(RefCast)]
399#[repr(transparent)]
400struct ModalVDblGraph<Kind>(ModalDblTheory<Kind>);
401
402type ModalVDblComputad<'a, Kind> = AVDCComputad<
403 'a,
404 ModalObType,
405 ModalObOp,
406 ModalMorType,
407 ModalObTypes<Kind>,
408 UnderlyingGraph<ModalOneTheory<Kind>>,
409 ModalMorTypeGraph<Kind>,
410 QualifiedName,
411>;
412
413impl<Kind: DblTheoryKind> Validate for ModalVDblGraph<Kind> {
414 type ValidationError = InvalidVDblGraph<QualifiedName, QualifiedName, QualifiedName>;
415
416 fn validate(&self) -> Result<(), nonempty::NonEmpty<Self::ValidationError>> {
417 let edge_cptd = self.0.tight_computad();
418 let edge_errors = edge_cptd.iter_invalid().map(|err| match err {
419 InvalidGraph::Src(e) => InvalidVDblGraph::Dom(e),
420 InvalidGraph::Tgt(e) => InvalidVDblGraph::Cod(e),
421 });
422 let proedge_cptd = self.0.loose_computad();
423 let proedge_errors = proedge_cptd.iter_invalid().map(|err| match err {
424 InvalidGraph::Src(p) => InvalidVDblGraph::Src(p),
425 InvalidGraph::Tgt(p) => InvalidVDblGraph::Tgt(p),
426 });
427 validate::wrap_errors(edge_errors.chain(proedge_errors))?;
429
430 validate::wrap_errors(self.0.dbl_computad().iter_invalid())
431 }
432}
433
434impl<Kind: DblTheoryKind> VDblGraph for ModalVDblGraph<Kind> {
435 type V = ModalObType;
436 type E = ModalObOp;
437 type ProE = ModalMorType;
438 type Sq = ModalNode;
439
440 fn has_vertex(&self, x: &Self::V) -> bool {
441 ModalObTypes::ref_cast(&self.0).contains(x)
442 }
443 fn has_edge(&self, path: &Self::E) -> bool {
444 ModalOneTheory::ref_cast(&self.0).has_mor(path)
445 }
446 fn has_proedge(&self, path: &Self::ProE) -> bool {
447 ModalMorTypeGraph::ref_cast(&self.0).has_edge(path)
448 }
449 fn has_square(&self, node: &Self::Sq) -> bool {
450 match node {
451 ModalNode::Basic(app) => match &app.arg {
452 ModalOp::Generator(sq) => self.0.dbl_computad().has_square(sq),
453 ModalOp::Concat(_, _, p) => ModalProedgeGraph::ref_cast(&self.0).has_edge(p),
454 },
455 ModalNode::Unit(f) => ModalEdgeGraph::ref_cast(&self.0).has_edge(f),
456 ModalNode::Composite(path) => {
457 if path.len() <= 1 {
458 true
459 } else {
460 todo!("Non-trivial composites")
461 }
462 }
463 }
464 }
465
466 fn dom(&self, path: &Self::E) -> Self::V {
467 ModalOneTheory::ref_cast(&self.0).dom(path)
468 }
469 fn cod(&self, path: &Self::E) -> Self::V {
470 ModalOneTheory::ref_cast(&self.0).cod(path)
471 }
472 fn src(&self, path: &Self::ProE) -> Self::V {
473 ModalMorTypeGraph::ref_cast(&self.0).src(path)
474 }
475 fn tgt(&self, path: &Self::ProE) -> Self::V {
476 ModalMorTypeGraph::ref_cast(&self.0).tgt(path)
477 }
478
479 fn square_dom(&self, node: &Self::Sq) -> Path<Self::V, Self::ProE> {
480 match node {
481 ModalNode::Basic(app) => {
482 let ModeApp { arg, modalities } = app;
483 let dom = match &arg {
484 ModalOp::Generator(sq) => self.0.dbl_computad().square_dom(sq),
485 ModalOp::Concat(list, n, p) => {
486 let ob_type = p.clone().apply_all(repeat_n(Modality::List(*list), *n));
487 ShortPath::One(ob_type).into()
488 }
489 };
490 dom.map(|x| x.apply_all(modalities.clone()), |p| p.apply_all(modalities.clone()))
491 }
492 ModalNode::Unit(f) => {
493 ModalMorType::Zero(ModalEdgeGraph::ref_cast(&self.0).src(f)).into()
494 }
495 ModalNode::Composite(path) => path.clone(),
496 }
497 }
498 fn square_cod(&self, node: &Self::Sq) -> Self::ProE {
499 match node {
500 ModalNode::Basic(app) => {
501 let cod = match &app.arg {
502 ModalOp::Generator(sq) => self.0.dbl_computad().square_cod(sq),
503 ModalOp::Concat(list, _, p) => p.clone().apply(Modality::List(*list)).into(),
504 };
505 cod.apply_all(app.modalities.clone())
506 }
507 ModalNode::Unit(f) => ModalMorType::Zero(ModalEdgeGraph::ref_cast(&self.0).tgt(f)),
508 ModalNode::Composite(path) => {
509 self.0.composite(path.clone()).expect("Composite should exist")
510 }
511 }
512 }
513 fn square_src(&self, node: &Self::Sq) -> Self::E {
514 match node {
515 ModalNode::Basic(app) => {
516 let src = match &app.arg {
517 ModalOp::Generator(sq) => self.0.dbl_computad().square_src(sq),
518 ModalOp::Concat(list, n, p) => {
519 let graph = ModalProedgeGraph::ref_cast(&self.0);
520 ModeApp::new(ModalOp::Concat(*list, *n, graph.src(p))).into()
521 }
522 };
523 src.apply_all(app.modalities.clone())
524 }
525 ModalNode::Unit(f) => f.clone().into(),
526 ModalNode::Composite(path) => {
527 Path::empty(path.src(ModalMorTypeGraph::ref_cast(&self.0)))
528 }
529 }
530 }
531 fn square_tgt(&self, node: &Self::Sq) -> Self::E {
532 match node {
533 ModalNode::Basic(app) => {
534 let tgt = match &app.arg {
535 ModalOp::Generator(sq) => self.0.dbl_computad().square_tgt(sq),
536 ModalOp::Concat(list, n, p) => {
537 let graph = ModalProedgeGraph::ref_cast(&self.0);
538 ModeApp::new(ModalOp::Concat(*list, *n, graph.tgt(p))).into()
539 }
540 };
541 tgt.apply_all(app.modalities.clone())
542 }
543 ModalNode::Unit(f) => f.clone().into(),
544 ModalNode::Composite(path) => {
545 Path::empty(path.tgt(ModalMorTypeGraph::ref_cast(&self.0)))
546 }
547 }
548 }
549 fn arity(&self, node: &Self::Sq) -> usize {
550 match node {
551 ModalNode::Basic(app) => match &app.arg {
552 ModalOp::Generator(sq) => self.0.dbl_computad().arity(sq),
553 ModalOp::Concat(_, _, _) => 1,
554 },
555 ModalNode::Unit(_) => 1,
556 ModalNode::Composite(path) => path.len(),
557 }
558 }
559}
560
561impl<Kind: DblTheoryKind> VDblCategory for ModalDblTheory<Kind> {
562 type Ob = ModalObType;
563 type Arr = ModalObOp;
564 type Pro = ModalMorType;
565 type Cell = ModalMorOp;
566
567 fn has_ob(&self, x: &Self::Ob) -> bool {
568 ModalVDblGraph::ref_cast(self).has_vertex(x)
569 }
570 fn has_arrow(&self, f: &Self::Arr) -> bool {
571 ModalVDblGraph::ref_cast(self).has_edge(f)
572 }
573 fn has_proarrow(&self, m: &Self::Pro) -> bool {
574 ModalVDblGraph::ref_cast(self).has_proedge(m)
575 }
576 fn has_cell(&self, tree: &Self::Cell) -> bool {
577 tree.contained_in(ModalVDblGraph::ref_cast(self))
578 }
579
580 fn dom(&self, f: &Self::Arr) -> Self::Ob {
581 ModalVDblGraph::ref_cast(self).dom(f)
582 }
583 fn cod(&self, f: &Self::Arr) -> Self::Ob {
584 ModalVDblGraph::ref_cast(self).cod(f)
585 }
586 fn src(&self, m: &Self::Pro) -> Self::Ob {
587 ModalVDblGraph::ref_cast(self).src(m)
588 }
589 fn tgt(&self, m: &Self::Pro) -> Self::Ob {
590 ModalVDblGraph::ref_cast(self).tgt(m)
591 }
592
593 fn cell_dom(&self, tree: &Self::Cell) -> Path<Self::Ob, Self::Pro> {
594 tree.dom(ModalVDblGraph::ref_cast(self))
595 }
596 fn cell_cod(&self, tree: &Self::Cell) -> Self::Pro {
597 tree.cod(ModalVDblGraph::ref_cast(self))
598 }
599 fn cell_src(&self, tree: &Self::Cell) -> Self::Arr {
600 self.compose(tree.src(ModalVDblGraph::ref_cast(self)))
601 }
602 fn cell_tgt(&self, tree: &Self::Cell) -> Self::Arr {
603 self.compose(tree.tgt(ModalVDblGraph::ref_cast(self)))
604 }
605 fn arity(&self, tree: &Self::Cell) -> usize {
606 tree.arity(ModalVDblGraph::ref_cast(self))
607 }
608
609 fn compose(&self, path: Path<Self::Ob, Self::Arr>) -> Self::Arr {
610 ModalOneTheory::ref_cast(self).compose(path)
611 }
612 fn compose_cells(&self, tree: DblTree<Self::Arr, Self::Pro, Self::Cell>) -> Self::Cell {
613 tree.flatten()
614 }
615}
616
617impl<Kind: DblTheoryKind> VDCWithComposites for ModalDblTheory<Kind> {
618 fn composite_ext(&self, path: Path<Self::Ob, Self::Pro>) -> Option<Self::Cell> {
619 if self.composite(path.clone()).is_some() {
620 let graph = ModalVDblGraph::ref_cast(self);
621 Some(DblTree::single(ModalNode::Composite(path), graph))
622 } else {
623 None
624 }
625 }
626
627 fn composite(&self, path: Path<Self::Ob, Self::Pro>) -> Option<Self::Pro> {
628 match path {
629 Path::Id(x) => Some(ShortPath::Zero(x)),
630 Path::Seq(ms) => {
631 if ms.len() == 1 {
632 Some(ms.head)
633 } else {
634 todo!("Non-trivial composites")
635 }
636 }
637 }
638 }
639
640 fn through_composite(
641 &self,
642 _cell: Self::Cell,
643 _range: std::ops::Range<usize>,
644 ) -> Option<Self::Cell> {
645 todo!("Universal property of composites")
646 }
647}
648
649crate::dbl::theory::impl_dbl_theory!(ModalDblTheory<Kind>);
650
651impl<Kind: DblTheoryKind> Validate for ModalDblTheory<Kind> {
652 type ValidationError = InvalidDblTheory;
653
654 fn validate(&self) -> Result<(), nonempty::NonEmpty<Self::ValidationError>> {
655 ModalVDblGraph::ref_cast(self)
657 .validate()
658 .map_err(|errs| errs.map(|err| err.into()))?;
659
660 let graph = ModalEdgeGraph::ref_cast(self);
662 let arr_errors = self.arr_equations.iter().enumerate().filter_map(|(id, eq)| {
663 let errs = eq.validate_in(graph).err()?;
664 Some(InvalidDblTheory::ObOpEq(id, errs))
665 });
666
667 let graph = ModalProedgeGraph::ref_cast(self);
669 let pro_errors =
670 self.pro_composites.iter().enumerate().filter_map(|(id, ((fst, snd), comp))| {
671 let eq = PathEq::new(Path::pair(fst.clone(), snd.clone()), comp.clone().into());
672 let errs = eq.validate_in(graph).err()?;
673 Some(InvalidDblTheory::MorTypeEq(id, errs))
674 });
675
676 validate::wrap_errors(arr_errors.chain(pro_errors))
677 }
678}
679
680impl<Kind: DblTheoryKind> ModalDblTheory<Kind> {
681 pub(super) fn loose_computad(
683 &self,
684 ) -> Computad<'_, ModalObType, ModalObTypes<Kind>, QualifiedName> {
685 Computad::new(ModalObTypes::ref_cast(self), &self.pro_generators)
686 }
687
688 pub(super) fn tight_computad(
690 &self,
691 ) -> Computad<'_, ModalObType, ModalObTypes<Kind>, QualifiedName> {
692 Computad::new(ModalObTypes::ref_cast(self), &self.arr_generators)
693 }
694
695 pub(super) fn dbl_computad(&self) -> ModalVDblComputad<'_, Kind> {
697 AVDCComputad::new(
698 ModalObTypes::ref_cast(self),
699 UnderlyingGraph::ref_cast(ModalOneTheory::ref_cast(self)),
700 ModalMorTypeGraph::ref_cast(self),
701 &self.cell_generators,
702 )
703 }
704
705 pub fn add_ob_type(&mut self, id: QualifiedName) {
707 self.ob_generators.insert(id);
708 }
709
710 pub fn add_mor_type(&mut self, id: QualifiedName, src: ModalObType, tgt: ModalObType) {
712 self.pro_generators.add_edge(id, src, tgt);
713 }
714
715 pub fn add_ob_op(&mut self, id: QualifiedName, dom: ModalObType, cod: ModalObType) {
717 self.arr_generators.add_edge(id, dom, cod);
718 }
719
720 pub fn add_mor_op(
722 &mut self,
723 id: QualifiedName,
724 dom: Path<ModalObType, ModalMorType>,
725 cod: ModalMorType,
726 src: ModalObOp,
727 tgt: ModalObOp,
728 ) {
729 self.cell_generators.add_square(id, dom, cod.into(), src, tgt);
730 }
731
732 pub fn add_globular_mor_op(
734 &mut self,
735 id: QualifiedName,
736 dom: Path<ModalObType, ModalMorType>,
737 cod: ModalMorType,
738 ) {
739 let src = self.src(&cod); let tgt = self.tgt(&cod); self.add_mor_op(id, dom, cod, Path::Id(src), Path::Id(tgt));
742 }
743
744 pub fn add_special_mor_op(&mut self, id: QualifiedName, src: ModalObOp, tgt: ModalObOp) {
746 let dom = self.dom(&src); let cod = self.cod(&src); self.add_mor_op(id, Path::empty(dom), ShortPath::Zero(cod), src, tgt);
749 }
750
751 pub fn equate_ob_ops(&mut self, lhs: ModalObOp, rhs: ModalObOp) {
753 self.arr_equations.push(PathEq::new(lhs, rhs))
754 }
755
756 pub fn set_composite(&mut self, fst: ModalType, snd: ModalType, composite: ModalMorType) {
758 self.pro_composites.insert((fst, snd), composite);
759 }
760}