use core::marker::PhantomData;
pub trait Property {
const NAME: &'static str;
}
pub trait Implies<Q> {}
pub struct IsAssociative;
impl Property for IsAssociative {
const NAME: &'static str = "associativity";
}
pub struct HasLeftIdentity;
impl Property for HasLeftIdentity {
const NAME: &'static str = "left identity";
}
pub struct HasRightIdentity;
impl Property for HasRightIdentity {
const NAME: &'static str = "right identity";
}
pub struct IsMonoid;
impl Property for IsMonoid {
const NAME: &'static str = "monoid";
}
impl Implies<IsAssociative> for IsMonoid {}
impl Implies<HasLeftIdentity> for IsMonoid {}
impl Implies<HasRightIdentity> for IsMonoid {}
pub struct HasLeftInverse;
impl Property for HasLeftInverse {
const NAME: &'static str = "left inverse";
}
pub struct HasRightInverse;
impl Property for HasRightInverse {
const NAME: &'static str = "right inverse";
}
pub struct IsGroup;
impl Property for IsGroup {
const NAME: &'static str = "group";
}
impl Implies<IsMonoid> for IsGroup {}
impl Implies<IsAssociative> for IsGroup {}
impl Implies<HasLeftIdentity> for IsGroup {}
impl Implies<HasRightIdentity> for IsGroup {}
impl Implies<HasLeftInverse> for IsGroup {}
impl Implies<HasRightInverse> for IsGroup {}
pub struct IsCommutative;
impl Property for IsCommutative {
const NAME: &'static str = "commutativity";
}
pub struct IsAbelianGroup;
impl Property for IsAbelianGroup {
const NAME: &'static str = "abelian group";
}
impl Implies<IsGroup> for IsAbelianGroup {}
impl Implies<IsMonoid> for IsAbelianGroup {}
impl Implies<IsAssociative> for IsAbelianGroup {}
impl Implies<IsCommutative> for IsAbelianGroup {}
impl Implies<HasLeftInverse> for IsAbelianGroup {}
impl Implies<HasRightInverse> for IsAbelianGroup {}
impl Implies<HasLeftIdentity> for IsAbelianGroup {}
impl Implies<HasRightIdentity> for IsAbelianGroup {}
pub struct AdditivelyCommutative;
impl Property for AdditivelyCommutative {
const NAME: &'static str = "additively commutative";
}
pub struct IsDistributive;
impl Property for IsDistributive {
const NAME: &'static str = "distributive";
}
pub struct ZeroAnnihilates;
impl Property for ZeroAnnihilates {
const NAME: &'static str = "zero annihilation";
}
pub struct IsSemiring;
impl Property for IsSemiring {
const NAME: &'static str = "semiring";
}
impl Implies<AdditivelyCommutative> for IsSemiring {}
impl Implies<IsDistributive> for IsSemiring {}
impl Implies<ZeroAnnihilates> for IsSemiring {}
pub struct IsRing;
impl Property for IsRing {
const NAME: &'static str = "ring";
}
impl Implies<IsSemiring> for IsRing {}
impl Implies<AdditivelyCommutative> for IsRing {}
impl Implies<IsDistributive> for IsRing {}
pub struct IsField;
impl Property for IsField {
const NAME: &'static str = "field";
}
impl Implies<IsRing> for IsField {}
impl Implies<IsSemiring> for IsField {}
pub struct IsIdempotent;
impl Property for IsIdempotent {
const NAME: &'static str = "idempotent";
}
pub struct IsAbsorptive;
impl Property for IsAbsorptive {
const NAME: &'static str = "absorptive";
}
pub struct IsLattice;
impl Property for IsLattice {
const NAME: &'static str = "lattice";
}
impl Implies<IsIdempotent> for IsLattice {}
impl Implies<IsAbsorptive> for IsLattice {}
impl Implies<IsCommutative> for IsLattice {}
impl Implies<IsAssociative> for IsLattice {}
pub struct IsBoundedLattice;
impl Property for IsBoundedLattice {
const NAME: &'static str = "bounded lattice";
}
impl Implies<IsLattice> for IsBoundedLattice {}
impl Implies<IsIdempotent> for IsBoundedLattice {}
impl Implies<IsAbsorptive> for IsBoundedLattice {}
pub struct FunctorIdentity;
impl Property for FunctorIdentity {
const NAME: &'static str = "functor identity";
}
pub struct FunctorComposition;
impl Property for FunctorComposition {
const NAME: &'static str = "functor composition";
}
pub struct IsLawfulFunctor;
impl Property for IsLawfulFunctor {
const NAME: &'static str = "lawful functor";
}
impl Implies<FunctorIdentity> for IsLawfulFunctor {}
impl Implies<FunctorComposition> for IsLawfulFunctor {}
pub struct MonadLeftIdentity;
impl Property for MonadLeftIdentity {
const NAME: &'static str = "monad left identity";
}
pub struct MonadRightIdentity;
impl Property for MonadRightIdentity {
const NAME: &'static str = "monad right identity";
}
pub struct MonadAssociativity;
impl Property for MonadAssociativity {
const NAME: &'static str = "monad associativity";
}
pub struct IsLawfulMonad;
impl Property for IsLawfulMonad {
const NAME: &'static str = "lawful monad";
}
impl Implies<IsLawfulFunctor> for IsLawfulMonad {}
impl Implies<MonadLeftIdentity> for IsLawfulMonad {}
impl Implies<MonadRightIdentity> for IsLawfulMonad {}
impl Implies<MonadAssociativity> for IsLawfulMonad {}
pub struct And<P, Q>(PhantomData<(P, Q)>);
impl<P: Property, Q: Property> Property for And<P, Q> {
const NAME: &'static str = "conjunction";
}
impl<P, Q> Implies<P> for And<P, Q> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn property_names() {
assert_eq!(IsAssociative::NAME, "associativity");
assert_eq!(IsMonoid::NAME, "monoid");
assert_eq!(IsGroup::NAME, "group");
assert_eq!(IsAbelianGroup::NAME, "abelian group");
assert_eq!(IsSemiring::NAME, "semiring");
assert_eq!(IsRing::NAME, "ring");
assert_eq!(IsField::NAME, "field");
assert_eq!(IsLattice::NAME, "lattice");
assert_eq!(IsBoundedLattice::NAME, "bounded lattice");
assert_eq!(IsLawfulFunctor::NAME, "lawful functor");
assert_eq!(IsLawfulMonad::NAME, "lawful monad");
}
fn _assert_implies<P: Implies<Q>, Q>() {}
#[test]
fn implication_lattice_compiles() {
_assert_implies::<IsMonoid, IsAssociative>();
_assert_implies::<IsMonoid, HasLeftIdentity>();
_assert_implies::<IsGroup, IsMonoid>();
_assert_implies::<IsGroup, IsAssociative>();
_assert_implies::<IsAbelianGroup, IsGroup>();
_assert_implies::<IsAbelianGroup, IsCommutative>();
_assert_implies::<IsField, IsRing>();
_assert_implies::<IsRing, IsSemiring>();
_assert_implies::<IsBoundedLattice, IsLattice>();
_assert_implies::<IsLawfulMonad, IsLawfulFunctor>();
_assert_implies::<And<IsAssociative, IsCommutative>, IsAssociative>();
}
}