1use all_the_same::all_the_same;
4use derive_more::{From, TryInto};
5use tattle::display::SourceInfo;
6
7use std::rc::Rc;
8
9use super::{eval::*, prelude::*, text_elab, theory::*, toplevel::*, val::*};
10use crate::dbl::{
11 discrete::{self, DiscreteDblModelInstance, DiscreteInstanceTerm},
12 discrete_tabulator, modal,
13 model::{DblModel, DblModelPrinter, FpDblModel, MutDblModel},
14 theory::{DblTheory, DblTheoryKind, NonUnital, Unital},
15};
16use crate::one::{
17 Category,
18 path::{Path, PathEq},
19};
20use crate::zero::{Namespace, QualifiedName, SkelColumn};
21
22pub enum Model {
27 Discrete(Box<discrete::DiscreteDblModel>),
29 DiscreteTab(Box<discrete_tabulator::DiscreteTabModel>),
31 ModalUnital(Box<modal::ModalDblModel<Unital>>),
33 ModalNonUnital(Box<modal::ModalDblModel<NonUnital>>),
35}
36
37#[derive(Debug, From, TryInto)]
39enum Ob {
40 Discrete(QualifiedName),
41 DiscreteTab(discrete_tabulator::TabOb),
42 Modal(modal::ModalOb),
43}
44
45#[derive(Debug, From, TryInto)]
47enum Mor {
48 Discrete(Path<QualifiedName, QualifiedName>),
49 DiscreteTab(discrete_tabulator::TabMor),
50 Modal(modal::ModalMor),
51}
52
53impl Model {
54 pub fn new(theory: &TheoryDef) -> Self {
56 match theory {
57 TheoryDef::Discrete(theory) => {
58 Model::Discrete(Box::new(discrete::DiscreteDblModel::new(theory.clone())))
59 }
60 TheoryDef::DiscreteTab(theory) => Model::DiscreteTab(Box::new(
61 discrete_tabulator::DiscreteTabModel::new(theory.clone()),
62 )),
63 TheoryDef::ModalUnital(theory) => {
64 Model::ModalUnital(Box::new(modal::ModalDblModel::new(theory.clone())))
65 }
66 TheoryDef::ModalNonUnital(theory) => {
67 Model::ModalNonUnital(Box::new(modal::ModalDblModel::new(theory.clone())))
68 }
69 }
70 }
71
72 pub fn from_text(th: &TheoryDef, s: &str) -> Result<Self, String> {
76 let theory = Theory::new("_".into(), th.clone());
77 let reporter = Reporter::new();
78 let toplevel: Toplevel = Default::default();
79 let maybe_elab = text_elab::TT_PARSE_CONFIG.with_parsed(s, reporter.clone(), |fntn| {
80 let mut elaborator = text_elab::Elaborator::new(theory, reporter.clone(), &toplevel);
81 Some(elaborator.ty(fntn))
82 });
83 if let Some((_, ty_v)) = maybe_elab
84 && !reporter.errored()
85 {
86 let (model, _) = Self::from_ty(&toplevel, th, &ty_v);
87 Ok(model)
88 } else {
89 let source_info = SourceInfo::new(None, s);
90 Err(source_info.extract_report_to_string(reporter))
91 }
92 }
93
94 pub fn from_ty(toplevel: &Toplevel, th: &TheoryDef, ty: &BaseTyV) -> (Self, Namespace) {
98 let mut generator = ModelGenerator::new(toplevel, th);
99 let namespace = generator.generate(ty);
100 (generator.model, namespace)
101 }
102
103 pub fn as_discrete(self) -> Option<discrete::DiscreteDblModel> {
105 match self {
106 Model::Discrete(model) => Some(*model),
107 _ => None,
108 }
109 }
110
111 pub fn as_modal(self) -> Option<modal::ModalDblModel<Unital>> {
113 match self {
114 Model::ModalUnital(model) => Some(*model),
115 _ => None,
116 }
117 }
118
119 pub fn as_modal_non_unital(self) -> Option<modal::ModalDblModel<NonUnital>> {
121 match self {
122 Model::ModalNonUnital(model) => Some(*model),
123 _ => None,
124 }
125 }
126
127 fn id(&self, ob: Ob) -> Mor {
129 all_the_same!(match self {
130 Model::[Discrete, ModalUnital, DiscreteTab, ModalNonUnital](model) => {
131 model.id(ob.try_into().unwrap()).into()
132 }
133 })
134 }
135
136 fn compose2(&self, mor1: Mor, mor2: Mor) -> Mor {
138 all_the_same!(match self {
139 Model::[Discrete, DiscreteTab, ModalUnital, ModalNonUnital](model) => {
140 model.compose2(mor1.try_into().unwrap(), mor2.try_into().unwrap()).into()
141 }
142 })
143 }
144
145 fn tabulated(&self, mor: Mor) -> Option<Ob> {
147 match self {
148 Model::Discrete(_) => None,
149 Model::DiscreteTab(model) => Some(model.tabulated(mor.try_into().unwrap()).into()),
150 Model::ModalUnital(_) | Model::ModalNonUnital(_) => None,
151 }
152 }
153
154 fn add_ob(&mut self, name: QualifiedName, ob_type: ObType) {
156 all_the_same!(match self {
157 Model::[Discrete, DiscreteTab, ModalUnital, ModalNonUnital](model) => {
158 model.add_ob(name, ob_type.try_into().unwrap())
159 }
160 });
161 }
162
163 fn add_mor(&mut self, name: QualifiedName, dom: Ob, cod: Ob, mor_type: MorType) {
165 all_the_same!(match self {
166 Model::[Discrete, DiscreteTab, ModalUnital, ModalNonUnital](model) => {
167 model.add_mor(
168 name,
169 dom.try_into().unwrap(),
170 cod.try_into().unwrap(),
171 mor_type.try_into().unwrap()
172 );
173 }
174 });
175 }
176
177 fn add_equation(&mut self, lhs: Mor, rhs: Mor) {
179 match self {
180 Model::Discrete(model) => {
181 model.add_equation(PathEq::new(lhs.try_into().unwrap(), rhs.try_into().unwrap()));
182 }
183 Model::DiscreteTab(_) => {
184 }
186 Model::ModalUnital(_) | Model::ModalNonUnital(_) => {
187 }
189 }
190 }
191
192 pub fn summary(&self, printer: &DblModelPrinter) -> String {
194 all_the_same!(match self {
195 Model::[Discrete, DiscreteTab, ModalUnital, ModalNonUnital](model) => printer.summary(model.as_ref())
196 })
197 }
198
199 pub fn to_doc<'a>(&self, printer: &DblModelPrinter, ns: &Namespace) -> D<'a> {
201 all_the_same!(match self {
202 Model::[Discrete, DiscreteTab, ModalUnital, ModalNonUnital](model) => printer.namespaced_doc(model.as_ref(), ns, ns)
203 })
204 }
205}
206
207struct ModelGenerator<'a> {
208 eval: Evaluator<'a>,
209 theory: TheoryDef,
210 model: Model,
211}
212
213impl<'a> ModelGenerator<'a> {
214 fn new(toplevel: &'a Toplevel, theory: &TheoryDef) -> Self {
215 let eval = Evaluator::empty(toplevel);
216 let theory = theory.clone();
217 let model = Model::new(&theory);
218 Self { eval, theory, model }
219 }
220
221 fn generate(&mut self, ty: &BaseTyV) -> Namespace {
222 let tm_n;
223 (tm_n, self.eval) = self.eval.bind_self(ty.clone());
224 let tm_v = self.eval.eta_neu(&tm_n, ty);
225 self.extract(vec![], &tm_v, ty).unwrap_or_else(Namespace::new_for_uuid)
226 }
227
228 fn mor_generator(&self, name: QualifiedName) -> Mor {
230 match &self.model {
231 Model::Discrete(_) => Mor::Discrete(Path::single(name)),
232 Model::DiscreteTab(_) => Mor::DiscreteTab(name.into()),
233 Model::ModalUnital(_) | Model::ModalNonUnital(_) => {
234 Mor::Modal(modal::ModalMor::Generator(name))
235 }
236 }
237 }
238
239 fn ob_app(&self, name: &NameSegment, tm_v: &BaseTmV) -> Option<(Ob, ObType)> {
243 let name: QualifiedName = [*name].into();
244 match &self.model {
245 Model::Discrete(_) | Model::DiscreteTab(_) => None,
246 Model::ModalUnital(model) => self.op_app_modal(model, name, tm_v),
247 Model::ModalNonUnital(model) => self.op_app_modal(model, name, tm_v),
248 }
249 }
250
251 fn op_app_modal<Kind: DblTheoryKind>(
252 &self,
253 model: &modal::ModalDblModel<Kind>,
254 name: QualifiedName,
255 tm_v: &BaseTmV,
256 ) -> Option<(Ob, ObType)> {
257 let theory = model.theory();
258 let op = modal::ModalObOp::generator(name.clone());
259 let ot: ObType = theory.ob_op_cod(&op).into();
260 let ob = self.make_ob_check_type(tm_v, &theory.ob_op_dom(&op).into())?;
261 let ob = ob.try_into().unwrap();
262 Some((Ob::Modal(modal::ModalOb::App(Box::new(ob), name)), ot))
263 }
264
265 fn ob_list(&self, elems: Vec<Ob>, ob_type: &ObType) -> Option<Ob> {
267 match &self.model {
268 Model::Discrete(_) | Model::DiscreteTab(_) => None,
269 Model::ModalUnital(_) | Model::ModalNonUnital(_) => {
270 let ob_type: &modal::ModalObType = ob_type.try_into().unwrap();
271 let Some(modal::Modality::List(list_type)) = ob_type.modalities.last() else {
272 unreachable!() };
274 Some(Ob::Modal(modal::ModalOb::List(
275 *list_type,
276 elems.into_iter().map(|ob| ob.try_into().unwrap()).collect(),
277 )))
278 }
279 }
280 }
281
282 fn make_ob_synth_type(&self, val: &BaseTmV) -> Option<(Ob, ObType)> {
286 match &**val {
287 BaseTmV_::Neu(n, ty_v) => {
288 let BaseTyV_::Object(ob_type) = &**ty_v else {
289 return None;
290 };
291 let name = n.to_qualified_name();
292 let ob = match &self.model {
293 Model::Discrete(_) => Ob::Discrete(name),
294 Model::DiscreteTab(_) => Ob::DiscreteTab(name.into()),
295 Model::ModalUnital(_) | Model::ModalNonUnital(_) => {
296 Ob::Modal(modal::ModalOb::Generator(name))
297 }
298 };
299 Some((ob, ob_type.clone()))
300 }
301 BaseTmV_::App(name, tm_v) => self.ob_app(name, tm_v),
302 BaseTmV_::Tab(mor_tm_v) => {
303 let (mor, mor_type) = self.synth_mor(mor_tm_v)?;
304 Some((self.model.tabulated(mor)?, self.theory.tabulator(mor_type)?))
305 }
306 _ => None,
307 }
308 }
309
310 fn make_ob_check_type(&self, val: &BaseTmV, ob_type: &ObType) -> Option<Ob> {
314 match &**val {
315 BaseTmV_::List(elems) => {
317 let el_type = ob_type.clone().list_arg()?;
318 let elems: Option<Vec<_>> =
319 elems.iter().map(|tm| self.make_ob_check_type(tm, &el_type)).collect();
320 self.ob_list(elems?, ob_type)
321 }
322 _ => {
323 let (ob, ot) = self.make_ob_synth_type(val)?;
324 (ot == *ob_type).then_some(ob)
325 }
326 }
327 }
328
329 fn synth_mor(&self, val: &BaseTmV) -> Option<(Mor, MorType)> {
333 match &**val {
334 BaseTmV_::Neu(n, ty_v) => {
335 let BaseTyV_::Morphism(mor_type, _, _) = &**ty_v else {
336 return None;
337 };
338 let name = n.to_qualified_name();
339 Some((self.mor_generator(name), mor_type.clone()))
340 }
341 BaseTmV_::Id(x) => {
342 let (dom, dom_type) = self.make_ob_synth_type(x)?;
343 let mor_type = self.theory.hom_type(dom_type)?;
344 Some((self.model.id(dom), mor_type))
345 }
346 BaseTmV_::Compose(f, g) => {
347 let (mf, mtf) = self.synth_mor(f)?;
348 let (mg, mtg) = self.synth_mor(g)?;
349 Some((self.model.compose2(mf, mg), self.theory.compose_types2(mtf, mtg)?))
350 }
351 _ => None,
352 }
353 }
354
355 fn make_mor(&self, val: &BaseTmV, mor_type: &MorType) -> Option<Mor> {
360 let (mor, mt) = self.synth_mor(val)?;
361 (mt == *mor_type).then_some(mor)
362 }
363
364 fn extract(
365 &mut self,
366 prefix: Vec<NameSegment>,
367 val: &BaseTmV,
368 ty: &BaseTyV,
369 ) -> Option<Namespace> {
370 match &**ty {
371 BaseTyV_::Object(ot) => {
372 self.model.add_ob(prefix.into(), ot.clone());
373 None
374 }
375 BaseTyV_::Morphism(mt, dom, cod) => {
376 let dom = self.make_ob_check_type(dom, &self.theory.src_type(mt))?;
377 let cod = self.make_ob_check_type(cod, &self.theory.tgt_type(mt))?;
378 self.model.add_mor(prefix.into(), dom, cod, mt.clone());
379 None
380 }
381 BaseTyV_::Record(r) => {
382 let mut namespace = Namespace::new_for_uuid();
383 for (name, (label, _)) in r.fields.iter() {
384 let mut prefix = prefix.clone();
385 prefix.push(*name);
386 if let NameSegment::Uuid(uuid) = name {
387 namespace.set_label(*uuid, *label);
388 }
389 let field_tm_v = self.eval.proj(val, *name, *label);
390 let field_ty_v = self.eval.field_ty(ty, val, *name);
391 if let Some(inner) = self.extract(prefix, &field_tm_v, &field_ty_v) {
392 namespace.add_inner(*name, inner);
393 };
394 }
395 Some(namespace)
396 }
397 BaseTyV_::Sing(_, _) => None,
398 BaseTyV_::Id(mor_ty, lhs, rhs) => {
399 let BaseTyV_::Morphism(mt, _, _) = &**mor_ty else {
400 return None;
401 };
402 if let (Some(lhs), Some(rhs)) = (self.make_mor(lhs, mt), self.make_mor(rhs, mt)) {
403 self.model.add_equation(lhs, rhs);
404 }
405 None
406 }
407 BaseTyV_::Meta(_) => None,
408 }
409 }
410
411 fn instance(
414 &self,
415 fields: &Row<FiberTyV>,
416 namespace: &mut Namespace,
417 ) -> Result<ModelInstance, String> {
418 match &self.model {
419 Model::Discrete(model) => {
420 let mut instance = DiscreteDblModelInstance::new(Rc::new((**model).clone()));
421 extract_instance_record(&mut instance, namespace, &[], fields)?;
422 Ok(ModelInstance::Discrete(instance))
423 }
424 Model::DiscreteTab(_) => {
425 Err("instance generation does not support discrete tabulator theories".into())
426 }
427 Model::ModalUnital(model) => {
428 Ok(ModelInstance::ModalUnital(self.modal_instance(model, namespace, fields)?))
429 }
430 Model::ModalNonUnital(model) => {
431 Ok(ModelInstance::ModalNonUnital(self.modal_instance(model, namespace, fields)?))
432 }
433 }
434 }
435
436 fn modal_instance<Kind: DblTheoryKind + Clone>(
439 &self,
440 model: &modal::ModalDblModel<Kind>,
441 namespace: &mut Namespace,
442 fields: &Row<FiberTyV>,
443 ) -> Result<modal::ModalDblModelInstance<Kind>, String> {
444 let mut instance = modal::ModalDblModelInstance::new(Rc::new(model.clone()));
445 self.extract_modal_record(&mut instance, namespace, &[], fields)?;
446 Ok(instance)
447 }
448
449 fn extract_modal_record<Kind: DblTheoryKind>(
456 &self,
457 instance: &mut modal::ModalDblModelInstance<Kind>,
458 namespace: &mut Namespace,
459 prefix: &[NameSegment],
460 fields: &Row<FiberTyV>,
461 ) -> Result<(), String> {
462 for (name, (label, field_ty)) in fields.iter() {
463 match &**field_ty {
464 FiberTyV_::Over(obj) => {
465 if let NameSegment::Uuid(uuid) = name {
466 namespace.set_label(*uuid, *label);
467 }
468 let mut qsegs = prefix.to_vec();
469 qsegs.push(*name);
470 let qname: QualifiedName = qsegs.into();
471 let fiber = self.modal_fiber_ob(obj)?;
472 instance.add_generator(qname, fiber);
473 }
474 FiberTyV_::Record(sub_fields) => {
475 if let NameSegment::Uuid(uuid) = name {
476 namespace.set_label(*uuid, *label);
477 }
478 let mut sub_ns = Namespace::new_for_uuid();
482 let mut sub_prefix = prefix.to_vec();
483 sub_prefix.push(*name);
484 self.extract_modal_record(instance, &mut sub_ns, &sub_prefix, sub_fields)?;
485 namespace.add_inner(*name, sub_ns);
486 }
487 FiberTyV_::Id(_, _, _) => {}
488 }
489 }
490 for (_, (_, field_ty)) in fields.iter() {
491 if let FiberTyV_::Id(eq_ty, lhs, rhs) = &**field_ty {
492 let ob_type = self.modal_equation_ob_type(eq_ty)?;
493 let lhs_t = self.modal_instance_term(&*instance, lhs, &ob_type, prefix)?;
494 let rhs_t = self.modal_instance_term(&*instance, rhs, &ob_type, prefix)?;
495 instance.add_equation(lhs_t, rhs_t);
496 }
497 }
498 Ok(())
499 }
500
501 fn modal_fiber_ob(&self, obj: &BaseTmV) -> Result<modal::ModalOb, String> {
503 let (ob, _) = self.make_ob_synth_type(obj).ok_or_else(|| {
504 "instance generator lies over an object this doctrine cannot yet extract".to_string()
505 })?;
506 ob.try_into()
507 .map_err(|_| "expected a modal object as a generator's fiber".to_string())
508 }
509
510 fn modal_equation_ob_type(&self, eq_ty: &FiberTyV) -> Result<ObType, String> {
512 let FiberTyV_::Over(obj) = &**eq_ty else {
513 return Err("instance equation is not over an object".into());
514 };
515 let (_, ob_type) = self
516 .make_ob_synth_type(obj)
517 .ok_or_else(|| "cannot determine the type of an instance equation".to_string())?;
518 Ok(ob_type)
519 }
520
521 fn modal_instance_term<Kind: DblTheoryKind>(
523 &self,
524 instance: &modal::ModalDblModelInstance<Kind>,
525 tm: &FiberTmV,
526 expected: &ObType,
527 prefix: &[NameSegment],
528 ) -> Result<modal::ModalInstanceTerm, String> {
529 let (mor, base) = self.modal_mor_base(instance, tm, expected, prefix)?;
530 Ok(modal::ModalInstanceTerm { mor, base })
531 }
532
533 fn modal_mor_base<Kind: DblTheoryKind>(
543 &self,
544 instance: &modal::ModalDblModelInstance<Kind>,
545 tm: &FiberTmV,
546 expected: &ObType,
547 prefix: &[NameSegment],
548 ) -> Result<(modal::ModalMor, modal::ModalInstanceBase), String> {
549 use modal::{ModalInstanceBase, ModalMor, ModalOb, Modality, MorListData};
550 match &**tm {
551 FiberTmV_::Var(_, _, _) | FiberTmV_::Proj(_, _, _) => {
552 let mut segs = prefix.to_vec();
553 segs.extend(fiber_full_name(tm)?);
554 let qname: QualifiedName = segs.into();
555 let fiber = instance
556 .fiber_of(&qname)
557 .ok_or_else(|| format!("instance term mentions unknown generator {qname}"))?;
558 let id = instance.model().id(fiber.clone());
559 Ok((id, ModalInstanceBase::Generator(qname)))
560 }
561 FiberTmV_::List(elems) => {
562 let (modality, el_type) = expected
563 .clone()
564 .mode_app()
565 .ok_or_else(|| "expected a modal list type for a list term".to_string())?;
566 let Modality::List(list_ty) = modality else {
567 return Err("expected a list modality for a list term".into());
568 };
569 let mut mors = Vec::with_capacity(elems.len());
570 let mut bases = Vec::with_capacity(elems.len());
571 for elem in elems {
572 let (m, b) = self.modal_mor_base(instance, elem, &el_type, prefix)?;
573 mors.push(m);
574 bases.push(b);
575 }
576 let base = ModalInstanceBase::List(list_ty, bases);
577 let identity_obs: Option<Vec<&ModalOb>> =
580 mors.iter().map(modal::modal_mor_as_identity).collect();
581 if let Some(objs) = identity_obs {
582 let list_ob = ModalOb::List(list_ty, objs.into_iter().cloned().collect());
583 Ok((instance.model().id(list_ob), base))
584 } else {
585 let data = match list_ty {
586 modal::List::Plain => MorListData::Plain(),
587 modal::List::Symmetric => {
588 MorListData::Symmetric(SkelColumn::new((0..mors.len()).collect()))
589 }
590 other => {
591 return Err(format!(
592 "instance terms do not yet support the {other:?} list modality"
593 ));
594 }
595 };
596 Ok((ModalMor::List(data, mors), base))
597 }
598 }
599 FiberTmV_::OverApp(path, _cod, inner) => {
600 let qname: QualifiedName =
601 path.iter().map(|(seg, _)| *seg).collect::<Vec<_>>().into();
602 let mor = ModalMor::Generator(qname.clone());
603 let mor_type = MorType::Modal(instance.model().mor_generator_type(&qname));
604 let dom_ty = self.theory.src_type(&mor_type);
605 let (inner_mor, base) = self.modal_mor_base(instance, inner, &dom_ty, prefix)?;
606 let full = if modal::modal_mor_as_identity(&inner_mor).is_some() {
607 mor
608 } else {
609 instance.model().compose2(inner_mor, mor)
610 };
611 Ok((full, base))
612 }
613 FiberTmV_::ObApp(op, inner) => {
614 let op_name: QualifiedName = [*op].into();
615 let ob_op = modal::ModalObOp::generator(op_name.clone());
618 let dom_ty: ObType = instance.model().theory().ob_op_dom(&ob_op).into();
619 let (inner_mor, inner_base) =
620 self.modal_mor_base(instance, inner, &dom_ty, prefix)?;
621 let mor = match modal::modal_mor_as_identity(&inner_mor).cloned() {
627 Some(inner_ob) => {
628 let app_ob = ModalOb::App(Box::new(inner_ob), op_name.clone());
629 instance.model().id(app_ob)
630 }
631 None => ModalMor::HomApp(Box::new(inner_mor.into()), op_name.clone()),
632 };
633 let base = ModalInstanceBase::ObApp(op_name, Box::new(inner_base));
634 Ok((mor, base))
635 }
636 FiberTmV_::Meta(_) => Err("instance term contains an unresolved metavariable".into()),
637 }
638 }
639}
640
641pub enum ModelInstance {
646 Discrete(DiscreteDblModelInstance),
648 ModalUnital(modal::ModalDblModelInstance<Unital>),
650 ModalNonUnital(modal::ModalDblModelInstance<NonUnital>),
652}
653
654pub fn instance_from_def(
664 toplevel: &Toplevel,
665 th: &TheoryDef,
666 inst: &Instance,
667) -> Result<(ModelInstance, Namespace), String> {
668 let mut generator = ModelGenerator::new(toplevel, th);
669 let mut namespace = generator.generate(&inst.codomain);
673 let FiberTyV_::Record(fields) = &*inst.val else {
674 return Err("expected an instance (a fiber record)".into());
675 };
676 let instance = generator.instance(fields, &mut namespace)?;
677 Ok((instance, namespace))
678}
679
680pub enum NormalizedInstanceTerm {
684 Discrete(DiscreteInstanceTerm),
686 Modal(modal::ModalInstanceTerm),
688}
689
690impl NormalizedInstanceTerm {
691 pub fn render(&self) -> String {
699 match self {
700 NormalizedInstanceTerm::Discrete(t) => match &t.path {
701 Path::Id(_) => format!("{}", t.base),
702 Path::Seq(edges) => {
703 let parts: Vec<_> = edges.iter().map(|mor| format!("{mor}")).collect();
704 if parts.len() == 1 {
705 format!("{} @ {}", parts[0], t.base)
706 } else {
707 format!("({}) @ {}", parts.join(" ; "), t.base)
708 }
709 }
710 },
711 NormalizedInstanceTerm::Modal(t) => {
712 let base = render_modal_base(&t.base);
713 match modal::modal_mor_as_identity(&t.mor) {
714 Some(_) => base,
715 None => format!("{} @ {}", render_modal_mor(&t.mor), base),
716 }
717 }
718 }
719 }
720}
721
722fn render_modal_base(base: &modal::ModalInstanceBase) -> String {
725 match base {
726 modal::ModalInstanceBase::Generator(name) => format!("{name}"),
727 modal::ModalInstanceBase::List(_, bases) => {
728 let inner: Vec<_> = bases.iter().map(render_modal_base).collect();
729 format!("[{}]", inner.join(", "))
730 }
731 modal::ModalInstanceBase::ObApp(op, inner) => {
732 format!("@{op} {}", render_modal_base(inner))
733 }
734 }
735}
736
737fn render_modal_mor(mor: &modal::ModalMor) -> String {
741 match mor {
742 modal::ModalMor::Generator(name) => format!("{name}"),
743 modal::ModalMor::Composite(path) => {
744 let parts = render_modal_mor_path(path);
745 match parts.len() {
746 0 => "id".to_string(),
747 1 => parts.into_iter().next().unwrap(),
748 _ => format!("({})", parts.join(" ; ")),
749 }
750 }
751 modal::ModalMor::App(path, op) => {
752 format!("{op}({})", render_modal_mor_path(path).join(" ; "))
753 }
754 modal::ModalMor::HomApp(path, op) => {
755 format!("@{op} {}", render_modal_mor_path(path).join(" ; "))
756 }
757 modal::ModalMor::List(_, mors) => {
758 let inner: Vec<_> = mors.iter().map(render_modal_mor).collect();
759 format!("[{}]", inner.join(", "))
760 }
761 }
762}
763
764fn render_modal_mor_path(path: &Path<modal::ModalOb, modal::ModalMor>) -> Vec<String> {
766 match path {
767 Path::Id(_) => Vec::new(),
768 Path::Seq(edges) => edges.iter().map(render_modal_mor).collect(),
769 }
770}
771
772pub fn normalize_instance_term(
782 toplevel: &Toplevel,
783 th: &TheoryDef,
784 inst: &Instance,
785 tm: &FiberTmV,
786 over: &BaseTmV,
787) -> Result<NormalizedInstanceTerm, String> {
788 let mut generator = ModelGenerator::new(toplevel, th);
789 generator.generate(&inst.codomain);
790 let FiberTyV_::Record(fields) = &*inst.val else {
791 return Err("expected an instance (a fiber record)".into());
792 };
793 let mut namespace = Namespace::new_for_uuid();
796 match generator.instance(fields, &mut namespace)? {
797 ModelInstance::Discrete(instance) => {
798 let term = fiber_tm_to_discrete_instance_term(&instance, tm, &[])?;
799 Ok(NormalizedInstanceTerm::Discrete(term))
800 }
801 ModelInstance::ModalUnital(instance) => {
802 let (_, ob_type) = generator
803 .make_ob_synth_type(over)
804 .ok_or_else(|| "cannot determine the object the term lies over".to_string())?;
805 let term = generator.modal_instance_term(&instance, tm, &ob_type, &[])?;
806 Ok(NormalizedInstanceTerm::Modal(term))
807 }
808 ModelInstance::ModalNonUnital(instance) => {
809 let (_, ob_type) = generator
810 .make_ob_synth_type(over)
811 .ok_or_else(|| "cannot determine the object the term lies over".to_string())?;
812 let term = generator.modal_instance_term(&instance, tm, &ob_type, &[])?;
813 Ok(NormalizedInstanceTerm::Modal(term))
814 }
815 }
816}
817
818fn extract_instance_record(
827 instance: &mut DiscreteDblModelInstance,
828 namespace: &mut Namespace,
829 prefix: &[NameSegment],
830 fields: &Row<FiberTyV>,
831) -> Result<(), String> {
832 for (name, (label, field_ty)) in fields.iter() {
833 match &**field_ty {
834 FiberTyV_::Over(obj) => {
835 if let NameSegment::Uuid(uuid) = name {
836 namespace.set_label(*uuid, *label);
837 }
838 let mut qsegs = prefix.to_vec();
839 qsegs.push(*name);
840 let qname: QualifiedName = qsegs.into();
841 let BaseTmV_::Neu(n, _) = &**obj else {
846 return Err("model generation does not yet support generators over a modal \
847 object (list/tensor)"
848 .into());
849 };
850 instance.add_generator(qname, n.to_qualified_name());
851 }
852 FiberTyV_::Record(sub_fields) => {
853 if let NameSegment::Uuid(uuid) = name {
854 namespace.set_label(*uuid, *label);
855 }
856 let mut sub_ns = Namespace::new_for_uuid();
860 let mut sub_prefix = prefix.to_vec();
861 sub_prefix.push(*name);
862 extract_instance_record(instance, &mut sub_ns, &sub_prefix, sub_fields)?;
863 namespace.add_inner(*name, sub_ns);
864 }
865 FiberTyV_::Id(_, _, _) => {}
866 }
867 }
868 for (_, (_, field_ty)) in fields.iter() {
869 if let FiberTyV_::Id(_, lhs, rhs) = &**field_ty {
870 let lhs_t = fiber_tm_to_discrete_instance_term(instance, lhs, prefix)?;
871 let rhs_t = fiber_tm_to_discrete_instance_term(instance, rhs, prefix)?;
872 instance.add_equation(lhs_t, rhs_t);
873 }
874 }
875 Ok(())
876}
877
878fn fiber_tm_to_discrete_instance_term(
885 instance: &DiscreteDblModelInstance,
886 tm: &FiberTmV,
887 prefix: &[NameSegment],
888) -> Result<DiscreteInstanceTerm, String> {
889 let mut mors_outer_first: Vec<QualifiedName> = Vec::new();
891 let mut cur = tm;
892 let base: QualifiedName = loop {
893 match &**cur {
894 FiberTmV_::OverApp(mor_path, _, inner) => {
895 let name: QualifiedName =
896 mor_path.iter().map(|(seg, _)| *seg).collect::<Vec<_>>().into();
897 mors_outer_first.push(name);
898 cur = inner;
899 }
900 _ => {
901 let mut segs = prefix.to_vec();
902 segs.extend(fiber_full_name(cur)?);
903 break segs.into();
904 }
905 }
906 };
907 mors_outer_first.reverse();
909 let path = match Path::from_vec(mors_outer_first) {
910 Some(p) => p,
911 None => {
912 let fiber = instance
913 .fiber_of(&base)
914 .ok_or_else(|| format!("instance term mentions unknown generator {base}"))?;
915 Path::Id(fiber.clone())
916 }
917 };
918 Ok(DiscreteInstanceTerm { path, base })
919}
920
921fn fiber_full_name(tm: &FiberTmV) -> Result<Vec<NameSegment>, String> {
926 let mut segments = Vec::new();
927 let mut cur = tm;
928 loop {
929 match &**cur {
930 FiberTmV_::Var(_, name, _) => {
931 segments.push(*name);
932 break;
933 }
934 FiberTmV_::Proj(inner, f, _) => {
935 segments.push(*f);
936 cur = inner;
937 }
938 _ => return Err("expected a fiber generator or projection".into()),
939 }
940 }
941 segments.reverse();
942 Ok(segments)
943}
944
945#[cfg(test)]
946mod tests {
947 use super::*;
948 use crate::tt::text_elab::{TT_PARSE_CONFIG, TopElabResult, TopElaborator};
949 use crate::tt::theory::std_theories;
950
951 fn elaborate_to_toplevel(src: &str) -> Toplevel {
952 let reporter = Reporter::new();
953 let mut toplevel = Toplevel::new(std_theories());
954 let _ = TT_PARSE_CONFIG.with_parsed_top(src, reporter.clone(), |topntns| {
955 let mut topelab = TopElaborator::new(reporter.clone());
956 for topntn in topntns.iter() {
957 if let Some(TopElabResult::Declaration(name, decl)) =
958 topelab.elab(&toplevel, topntn)
959 {
960 toplevel.declarations.insert(name, decl);
961 }
962 }
963 Some(())
964 });
965 assert!(!reporter.errored(), "elaboration produced errors");
966 toplevel
967 }
968
969 #[test]
970 fn instance_over_weighted_graph() {
971 let src = r#"
972set_theory ThSchema
973
974model WeightedGraph := [
975 V : Entity,
976 E : Entity,
977 Weight : AttrType,
978 src : (Hom Entity)[E, V],
979 tgt : (Hom Entity)[E, V],
980 weight : Attr[E, Weight]
981]
982
983instance I : WeightedGraph := [
984 V := [v],
985 E := [e],
986 src(e) := v
987]
988"#;
989 let toplevel = elaborate_to_toplevel(src);
990 let def = match toplevel.declarations.get(&name_seg("I")) {
991 Some(TopDecl::Instance(i)) => i.clone(),
992 _ => panic!("expected I to be an instance declaration"),
993 };
994 let (instance, _ns) = instance_from_def(&toplevel, &def.theory.definition, &def).unwrap();
995 let ModelInstance::Discrete(instance) = instance else {
996 panic!("expected a discrete instance");
997 };
998
999 let e_qname: QualifiedName = vec![name_seg("e")].into();
1000 let e_fiber: QualifiedName = vec![name_seg("E")].into();
1001 assert_eq!(instance.fiber_of(&e_qname), Some(&e_fiber));
1002 assert_eq!(instance.equations().count(), 1);
1003 }
1004
1005 #[test]
1011 fn import_of_instance_with_fiber_equations() {
1012 let src = r#"
1013set_theory ThSchema
1014
1015model WeightedGraph := [
1016 V : Entity,
1017 E : Entity,
1018 Weight : AttrType,
1019 src : (Hom Entity)[E, V],
1020 tgt : (Hom Entity)[E, V],
1021 weight : Attr[E, Weight]
1022]
1023
1024instance Loop : WeightedGraph := [
1025 V := [v],
1026 E := [e],
1027 src(e) := v,
1028 tgt(e) := v
1029]
1030
1031instance UseLoop : WeightedGraph := [
1032 l : Loop
1033]
1034"#;
1035 let toplevel = elaborate_to_toplevel(src);
1036
1037 let loop_def = match toplevel.declarations.get(&name_seg("Loop")) {
1040 Some(TopDecl::Instance(i)) => i.clone(),
1041 _ => panic!("expected Loop to be an instance declaration"),
1042 };
1043 let FiberTyV_::Record(r) = &*loop_def.val else {
1044 panic!("Loop should be a fiber record");
1045 };
1046 assert!(r.get(name_seg("e")).is_some(), "generator field e");
1047 assert!(r.get(name_seg("v")).is_some(), "generator field v");
1048 assert!(r.get(name_seg("_eq0")).is_some(), "first fiber equation");
1049 assert!(r.get(name_seg("_eq1")).is_some(), "second fiber equation");
1050
1051 let use_def = match toplevel.declarations.get(&name_seg("UseLoop")) {
1054 Some(TopDecl::Instance(i)) => i.clone(),
1055 _ => panic!("expected UseLoop to be an instance declaration"),
1056 };
1057 let (instance, _ns) =
1058 instance_from_def(&toplevel, &use_def.theory.definition, &use_def).unwrap();
1059 let ModelInstance::Discrete(instance) = instance else {
1060 panic!("expected a discrete instance");
1061 };
1062 let le_qname: QualifiedName = vec![name_seg("l"), name_seg("e")].into();
1063 let e_fiber: QualifiedName = vec![name_seg("E")].into();
1064 assert_eq!(instance.fiber_of(&le_qname), Some(&e_fiber));
1065 assert_eq!(instance.equations().count(), 2);
1066 }
1067
1068 #[test]
1071 fn instance_over_multicategory_monoid() {
1072 let src = r#"
1073set_theory ThMulticategory
1074
1075model SigMonoid := [
1076 M : Object,
1077 op : Multihom[[M, M], M],
1078 unit : Multihom[[], M]
1079]
1080
1081instance Z2 : SigMonoid := [
1082 M := [x],
1083 op([x,x]) := unit([]),
1084 op([x,unit([])]) := x
1085]
1086"#;
1087 let toplevel = elaborate_to_toplevel(src);
1088 let def = match toplevel.declarations.get(&name_seg("Z2")) {
1089 Some(TopDecl::Instance(i)) => i.clone(),
1090 _ => panic!("expected Z2 to be an instance declaration"),
1091 };
1092 let (instance, _ns) = instance_from_def(&toplevel, &def.theory.definition, &def).unwrap();
1093 let ModelInstance::ModalUnital(instance) = instance else {
1094 panic!("expected a unital modal instance");
1095 };
1096
1097 let x_qname: QualifiedName = vec![name_seg("x")].into();
1098 let m_fiber = modal::ModalOb::Generator(vec![name_seg("M")].into());
1099 assert_eq!(instance.fiber_of(&x_qname), Some(&m_fiber));
1100 assert_eq!(instance.equations().count(), 2);
1101 }
1102
1103 #[test]
1106 fn instance_over_symmetric_monoidal() {
1107 let src = r#"
1108set_theory ThSymMonoidalCategory
1109
1110model AB := [
1111 A : Object,
1112 B : Object,
1113 f : (Hom Object)[@tensor [A, B], A]
1114]
1115
1116instance i : AB := [
1117 A := [a],
1118 B := [b],
1119 f(@tensor [a, b]) := a
1120]
1121"#;
1122 let toplevel = elaborate_to_toplevel(src);
1123 let def = match toplevel.declarations.get(&name_seg("i")) {
1124 Some(TopDecl::Instance(i)) => i.clone(),
1125 _ => panic!("expected i to be an instance declaration"),
1126 };
1127 let (instance, _ns) = instance_from_def(&toplevel, &def.theory.definition, &def).unwrap();
1128 let ModelInstance::ModalUnital(instance) = instance else {
1129 panic!("expected a unital modal instance");
1130 };
1131
1132 let a_qname: QualifiedName = vec![name_seg("a")].into();
1133 let a_fiber = modal::ModalOb::Generator(vec![name_seg("A")].into());
1134 assert_eq!(instance.fiber_of(&a_qname), Some(&a_fiber));
1135 assert_eq!(instance.equations().count(), 1);
1136 }
1137
1138 #[test]
1142 fn instance_over_symmetric_monoidal_functorial() {
1143 let src = r#"
1144set_theory ThSymMonoidalCategory
1145
1146model Chain := [
1147 X : Object,
1148 Y : Object,
1149 s : (Hom Object)[X, Y],
1150 t : (Hom Object)[@tensor [Y, Y], X]
1151]
1152
1153instance chain : Chain := [
1154 X := [x0],
1155 t(@tensor [s(x0), s(x0)]) := x0
1156]
1157"#;
1158 let toplevel = elaborate_to_toplevel(src);
1159 let def = match toplevel.declarations.get(&name_seg("chain")) {
1160 Some(TopDecl::Instance(i)) => i.clone(),
1161 _ => panic!("expected chain to be an instance declaration"),
1162 };
1163 let (instance, _ns) = instance_from_def(&toplevel, &def.theory.definition, &def).unwrap();
1164 let ModelInstance::ModalUnital(instance) = instance else {
1165 panic!("expected a unital modal instance");
1166 };
1167 assert_eq!(instance.equations().count(), 1);
1168
1169 let (lhs, _) = instance.equations().next().unwrap();
1172 assert!(
1173 matches!(&lhs.mor, modal::ModalMor::Composite(_)),
1174 "expected the applied morphism to compose `t` after the tensor's HomApp"
1175 );
1176 }
1177}