use super::category::Category;
use super::entity::Entity;
pub struct Yoneda<C: Category> {
pub object: C::Object,
pub morphisms: Vec<C::Morphism>,
}
impl<C: Category> Yoneda<C>
where
C::Object: Clone + PartialEq,
C::Morphism: Clone,
{
pub fn embed(obj: &C::Object) -> Self {
Self {
object: obj.clone(),
morphisms: C::morphisms_from(obj),
}
}
pub fn degree(&self) -> usize {
self.morphisms.len()
}
pub fn structurally_equivalent(a: &C::Object, b: &C::Object) -> bool {
C::morphisms_from(a).len() == C::morphisms_from(b).len()
&& C::morphisms_to(a).len() == C::morphisms_to(b).len()
}
}
pub struct CoYoneda<C: Category> {
pub object: C::Object,
pub morphisms: Vec<C::Morphism>,
}
impl<C: Category> CoYoneda<C>
where
C::Object: Clone + PartialEq,
C::Morphism: Clone,
{
pub fn embed(obj: &C::Object) -> Self {
Self {
object: obj.clone(),
morphisms: C::morphisms_to(obj),
}
}
pub fn degree(&self) -> usize {
self.morphisms.len()
}
}
pub struct YonedaProfile<C: Category> {
pub object: C::Object,
pub outgoing: Vec<C::Morphism>,
pub incoming: Vec<C::Morphism>,
}
impl<C: Category> YonedaProfile<C>
where
C::Object: Clone + PartialEq,
C::Morphism: Clone,
{
pub fn of(obj: &C::Object) -> Self {
Self {
object: obj.clone(),
outgoing: C::morphisms_from(obj),
incoming: C::morphisms_to(obj),
}
}
pub fn total_degree(&self) -> usize {
self.outgoing.len() + self.incoming.len()
}
}
pub fn full_yoneda<C: Category>() -> Vec<YonedaProfile<C>>
where
C::Object: Clone + PartialEq,
C::Morphism: Clone,
{
C::Object::variants()
.iter()
.map(|obj| YonedaProfile::of(obj))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::category::entity::Entity as EntityTrait;
use crate::category::relationship::Relationship;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum Node {
A,
B,
C,
}
impl EntityTrait for Node {
fn variants() -> Vec<Self> {
vec![Self::A, Self::B, Self::C]
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct Edge {
from: Node,
to: Node,
}
impl Relationship for Edge {
type Object = Node;
fn source(&self) -> Node {
self.from
}
fn target(&self) -> Node {
self.to
}
}
struct Graph;
impl Category for Graph {
type Object = Node;
type Morphism = Edge;
fn identity(obj: &Node) -> Edge {
Edge {
from: *obj,
to: *obj,
}
}
fn compose(f: &Edge, g: &Edge) -> Option<Edge> {
if f.to == g.from {
Some(Edge {
from: f.from,
to: g.to,
})
} else {
None
}
}
fn morphisms() -> Vec<Edge> {
vec![
Edge {
from: Node::A,
to: Node::B,
},
Edge {
from: Node::B,
to: Node::C,
},
Edge {
from: Node::A,
to: Node::A,
},
Edge {
from: Node::B,
to: Node::B,
},
Edge {
from: Node::C,
to: Node::C,
},
]
}
}
#[test]
fn yoneda_embed_collects_outgoing() {
let y = Yoneda::<Graph>::embed(&Node::A);
assert_eq!(y.degree(), 2);
}
#[test]
fn coyoneda_embed_collects_incoming() {
let cy = CoYoneda::<Graph>::embed(&Node::C);
assert_eq!(cy.degree(), 2);
}
#[test]
fn yoneda_profile_total_degree() {
let p = YonedaProfile::<Graph>::of(&Node::B);
assert_eq!(p.total_degree(), 4);
}
#[test]
fn full_yoneda_covers_all_objects() {
let profiles = full_yoneda::<Graph>();
assert_eq!(profiles.len(), 3); }
#[test]
fn yoneda_identity_principle() {
assert!(!Yoneda::<Graph>::structurally_equivalent(
&Node::A,
&Node::C
));
assert!(Yoneda::<Graph>::structurally_equivalent(&Node::A, &Node::A));
}
}