#[allow(unused_imports)]
use alloc::{vec, vec::Vec};
use core::marker::PhantomData;
use super::arrow::Arrow;
use super::category::Category;
use super::entity::Concept;
use super::functor::Functor;
use super::kinds::FunctorKind;
pub trait Quiver {
type Vertex: Concept;
type Edge: Arrow<Object = Self::Vertex>;
fn edges() -> Vec<Self::Edge>;
}
pub struct Path<Q: Quiver> {
source: Q::Vertex,
target: Q::Vertex,
edges: Vec<Q::Edge>,
}
impl<Q: Quiver> Clone for Path<Q> {
fn clone(&self) -> Self {
Self {
source: self.source.clone(),
target: self.target.clone(),
edges: self.edges.clone(),
}
}
}
impl<Q: Quiver> core::fmt::Debug for Path<Q> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Path")
.field("source", &self.source)
.field("target", &self.target)
.field("edges", &self.edges)
.finish()
}
}
impl<Q: Quiver> PartialEq for Path<Q> {
fn eq(&self, other: &Self) -> bool {
self.source == other.source && self.target == other.target && self.edges == other.edges
}
}
impl<Q: Quiver> Eq for Path<Q> {}
impl<Q: Quiver> Path<Q> {
pub fn empty(at: Q::Vertex) -> Self {
Self {
source: at.clone(),
target: at,
edges: Vec::new(),
}
}
pub fn edge(e: Q::Edge) -> Self {
Self {
source: e.source(),
target: e.target(),
edges: vec![e],
}
}
pub fn edges(&self) -> &[Q::Edge] {
&self.edges
}
pub fn len(&self) -> usize {
self.edges.len()
}
pub fn is_empty(&self) -> bool {
self.edges.is_empty()
}
}
impl<Q: Quiver> Arrow for Path<Q> {
type Object = Q::Vertex;
type Kind = ();
fn source(&self) -> Q::Vertex {
self.source.clone()
}
fn target(&self) -> Q::Vertex {
self.target.clone()
}
fn kind(&self) {}
}
pub struct FreeCategory<Q>(PhantomData<Q>);
impl<Q: Quiver> Category for FreeCategory<Q> {
type Object = Q::Vertex;
type Morphism = Path<Q>;
fn identity(obj: &Q::Vertex) -> Path<Q> {
Path::empty(obj.clone())
}
fn compose(f: &Path<Q>, g: &Path<Q>) -> Option<Path<Q>> {
if f.target != g.source {
return None;
}
let mut edges = f.edges.clone();
edges.extend(g.edges.iter().cloned());
Some(Path {
source: f.source.clone(),
target: g.target.clone(),
edges,
})
}
fn morphisms() -> Vec<Path<Q>> {
let mut ms: Vec<Path<Q>> = Q::Vertex::variants().into_iter().map(Path::empty).collect();
ms.extend(Q::edges().into_iter().map(Path::edge));
ms
}
}
pub trait QuiverInterpretation {
type Quiver: Quiver;
type Target: Category;
fn on_vertex(v: &<Self::Quiver as Quiver>::Vertex) -> <Self::Target as Category>::Object;
fn on_edge(e: &<Self::Quiver as Quiver>::Edge) -> <Self::Target as Category>::Morphism;
}
pub struct FreeExtension<I>(PhantomData<I>);
impl<I: QuiverInterpretation> Functor for FreeExtension<I> {
type Source = FreeCategory<I::Quiver>;
type Target = I::Target;
fn map_object(v: &<I::Quiver as Quiver>::Vertex) -> <I::Target as Category>::Object {
I::on_vertex(v)
}
fn map_morphism(path: &Path<I::Quiver>) -> <I::Target as Category>::Morphism {
let mut acc = I::Target::identity(&I::on_vertex(&path.source));
for e in &path.edges {
let img = I::on_edge(e);
acc = I::Target::compose(&acc, &img)
.expect("QuiverInterpretation edge images must compose along the path");
}
acc
}
const KIND: FunctorKind = FunctorKind::Free;
crate::relationship_meta!(
"FreeExtension",
"the unique functor extending a quiver interpretation over the free category (free-forgetful universal property)",
"Mac Lane (1971) Categories for the Working Mathematician II.7"
);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::category::laws::assert_functor_laws;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum V {
A,
B,
}
impl Concept for V {
fn variants() -> Vec<Self> {
vec![V::A, V::B]
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct E {
from: V,
to: V,
label: char,
}
impl Arrow for E {
type Object = V;
type Kind = ();
fn source(&self) -> V {
self.from
}
fn target(&self) -> V {
self.to
}
fn kind(&self) {}
}
struct TwoCycle;
impl Quiver for TwoCycle {
type Vertex = V;
type Edge = E;
fn edges() -> Vec<E> {
vec![
E {
from: V::A,
to: V::B,
label: 'f',
},
E {
from: V::B,
to: V::A,
label: 'g',
},
]
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct Reach {
from: V,
to: V,
}
impl Arrow for Reach {
type Object = V;
type Kind = ();
fn source(&self) -> V {
self.from
}
fn target(&self) -> V {
self.to
}
fn kind(&self) {}
}
struct ReachCat;
impl Category for ReachCat {
type Object = V;
type Morphism = Reach;
fn identity(o: &V) -> Reach {
Reach { from: *o, to: *o }
}
fn compose(f: &Reach, g: &Reach) -> Option<Reach> {
if f.to != g.from {
None
} else {
Some(Reach {
from: f.from,
to: g.to,
})
}
}
fn morphisms() -> Vec<Reach> {
let vs = V::variants();
vs.iter()
.flat_map(|&a| vs.iter().map(move |&b| Reach { from: a, to: b }))
.collect()
}
}
struct Collapse;
impl QuiverInterpretation for Collapse {
type Quiver = TwoCycle;
type Target = ReachCat;
fn on_vertex(v: &V) -> V {
*v
}
fn on_edge(e: &E) -> Reach {
Reach {
from: e.from,
to: e.to,
}
}
}
type Collapsed = FreeExtension<Collapse>;
#[test]
fn compose_concatenates_paths() {
let f = Path::<TwoCycle>::edge(E {
from: V::A,
to: V::B,
label: 'f',
});
let g = Path::<TwoCycle>::edge(E {
from: V::B,
to: V::A,
label: 'g',
});
let fg = FreeCategory::<TwoCycle>::compose(&f, &g).expect("f then g composes");
assert_eq!(fg.len(), 2);
assert_eq!(fg.source(), V::A);
assert_eq!(fg.target(), V::A);
assert!(FreeCategory::<TwoCycle>::compose(&f, &f).is_none());
}
#[test]
fn identity_is_the_empty_path() {
let id_a = FreeCategory::<TwoCycle>::identity(&V::A);
assert!(id_a.is_empty());
let f = Path::<TwoCycle>::edge(E {
from: V::A,
to: V::B,
label: 'f',
});
let id_b = FreeCategory::<TwoCycle>::identity(&V::B);
assert_eq!(
FreeCategory::<TwoCycle>::compose(&id_a, &f).as_ref(),
Some(&f)
);
assert_eq!(
FreeCategory::<TwoCycle>::compose(&f, &id_b).as_ref(),
Some(&f)
);
}
#[test]
fn free_extension_satisfies_functor_laws() {
assert_functor_laws::<Collapsed>();
}
#[test]
fn free_extension_folds_paths_through_the_target() {
let f = Path::<TwoCycle>::edge(E {
from: V::A,
to: V::B,
label: 'f',
});
let g = Path::<TwoCycle>::edge(E {
from: V::B,
to: V::A,
label: 'g',
});
let fg = FreeCategory::<TwoCycle>::compose(&f, &g).unwrap();
assert_eq!(
Collapsed::map_morphism(&fg),
Reach {
from: V::A,
to: V::A
}
);
}
}