pub trait Comonad: Sized {
type Item;
fn extract(&self) -> Self::Item;
fn extend<F, B>(&self, f: F) -> Self::Output<B>
where
F: Fn(&Self) -> B;
type Output<B>;
#[inline]
fn duplicate(&self) -> Self::Output<Self>
where
Self: Clone,
{
self.extend(core::clone::Clone::clone)
}
#[inline]
fn cmap<F, B>(&self, f: F) -> Self::Output<B>
where
F: Fn(Self::Item) -> B,
{
self.extend(move |w| f(w.extract()))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Identitas<A>(pub A);
impl<A> Identitas<A> {
#[inline]
pub fn new(value: A) -> Self {
Identitas(value)
}
#[inline]
pub fn value(&self) -> &A {
&self.0
}
#[inline]
pub fn into_value(self) -> A {
self.0
}
}
impl<A: Clone> Comonad for Identitas<A> {
type Item = A;
type Output<B> = Identitas<B>;
#[inline]
fn extract(&self) -> A {
self.0.clone()
}
#[inline]
fn extend<F, B>(&self, f: F) -> Identitas<B>
where
F: Fn(&Self) -> B,
{
Identitas(f(self))
}
}
#[derive(Clone)]
pub struct Thesaurus<S, A, F>
where
F: Fn(&S) -> A,
{
pub position: S,
pub peek_fn: F,
}
impl<S: Clone, A, F> Thesaurus<S, A, F>
where
F: Fn(&S) -> A,
{
#[inline]
pub fn new(position: S, peek_fn: F) -> Self {
Thesaurus { position, peek_fn }
}
#[inline]
pub fn peek(&self, pos: &S) -> A {
(self.peek_fn)(pos)
}
#[inline]
pub fn pos(&self) -> &S {
&self.position
}
#[inline]
pub fn seek(self, new_pos: S) -> Self {
Thesaurus {
position: new_pos,
peek_fn: self.peek_fn,
}
}
#[inline]
pub fn seeks<G>(self, f: G) -> Self
where
G: FnOnce(S) -> S,
{
Thesaurus {
position: f(self.position),
peek_fn: self.peek_fn,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Contextus<E, A> {
pub env: E,
pub value: A,
}
impl<E, A> Contextus<E, A> {
#[inline]
pub fn new(env: E, value: A) -> Self {
Contextus { env, value }
}
#[inline]
pub fn ask(&self) -> &E {
&self.env
}
#[inline]
pub fn val(&self) -> &A {
&self.value
}
}
impl<E: Clone, A: Clone> Comonad for Contextus<E, A> {
type Item = A;
type Output<B> = Contextus<E, B>;
#[inline]
fn extract(&self) -> A {
self.value.clone()
}
#[inline]
fn extend<F, B>(&self, f: F) -> Contextus<E, B>
where
F: Fn(&Self) -> B,
{
Contextus {
env: self.env.clone(),
value: f(self),
}
}
}
#[derive(Clone)]
pub struct Vestigium<M, A, F>
where
F: Fn(&M) -> A,
{
pub run: F,
_marker: core::marker::PhantomData<M>,
}
impl<M, A, F> Vestigium<M, A, F>
where
F: Fn(&M) -> A,
{
#[inline]
pub fn new(run: F) -> Self {
Vestigium {
run,
_marker: core::marker::PhantomData,
}
}
#[inline]
pub fn run_traced(&self, trace: &M) -> A {
(self.run)(trace)
}
}
#[cfg(test)]
mod tests {
use super::*;
extern crate std;
use std::string::{String, ToString};
#[test]
fn test_identitas_extract() {
let id = Identitas::new(42);
assert_eq!(id.extract(), 42);
}
#[test]
fn test_identitas_extend() {
let id = Identitas::new(10);
let result = id.extend(|w| w.extract() * 2);
assert_eq!(result.extract(), 20);
}
#[test]
fn test_identitas_duplicate() {
let id = Identitas::new(42);
let dup = id.duplicate();
assert_eq!(dup.extract().extract(), 42);
}
#[test]
fn test_identitas_cmap() {
let id = Identitas::new(5);
let result = id.cmap(|x| x * 3);
assert_eq!(result.extract(), 15);
}
#[test]
fn test_identitas_left_identity_law() {
let w = Identitas::new(42);
let result = w.extend(super::Comonad::extract);
assert_eq!(result.extract(), w.extract());
}
#[test]
fn test_identitas_right_identity_law() {
let w = Identitas::new(10);
let f = |x: &Identitas<i32>| x.extract() * 2;
assert_eq!(w.extend(f).extract(), f(&w));
}
#[test]
fn test_contextus_extract() {
let e = Contextus::new("config", 42);
assert_eq!(e.extract(), 42);
}
#[test]
fn test_contextus_ask() {
let e = Contextus::new("config", 42);
assert_eq!(e.ask(), &"config");
}
#[test]
fn test_contextus_extend() {
let e = Contextus::new(10, 5);
let result = e.extend(|w| w.extract() + *w.ask());
assert_eq!(result.extract(), 15);
assert_eq!(result.ask(), &10); }
#[test]
fn test_contextus_left_identity_law() {
let w = Contextus::new("env", 42);
let result = w.extend(super::Comonad::extract);
assert_eq!(result.extract(), w.extract());
}
#[test]
fn test_thesaurus_peek() {
let store = Thesaurus::new(0, |i: &i32| i * i);
assert_eq!(store.peek(&0), 0);
assert_eq!(store.peek(&5), 25);
assert_eq!(store.peek(&-3), 9);
}
#[test]
fn test_thesaurus_seek() {
let store = Thesaurus::new(0, |i: &i32| i * 2);
let moved = store.seek(5);
assert_eq!(moved.pos(), &5);
assert_eq!(moved.peek(&5), 10);
}
#[test]
fn test_thesaurus_seeks() {
let store = Thesaurus::new(5, |i: &i32| i * 2);
let modified = store.seeks(|x| x + 3);
assert_eq!(modified.pos(), &8);
}
#[test]
fn test_vestigium_run() {
let traced = Vestigium::new(|s: &String| s.len());
assert_eq!(traced.run_traced(&"hello".to_string()), 5);
assert_eq!(traced.run_traced(&"hi".to_string()), 2);
}
}