#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
use crate::typeclasses::hkt::{FunctorHKT, HKT};
#[cfg(feature = "alloc")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Liber<F: FunctorHKT, A> {
Purus(A),
Suspensus(Box<F::Target<Liber<F, A>>>),
}
#[cfg(feature = "alloc")]
impl<F: FunctorHKT, A> Liber<F, A> {
#[inline]
pub fn purus(a: A) -> Self {
Liber::Purus(a)
}
#[inline]
pub fn suspensus(fa: F::Target<Liber<F, A>>) -> Self {
Liber::Suspensus(Box::new(fa))
}
#[inline]
pub fn est_purus(&self) -> bool {
matches!(self, Liber::Purus(_))
}
#[inline]
pub fn est_suspensus(&self) -> bool {
matches!(self, Liber::Suspensus(_))
}
#[inline]
pub fn map<B, G>(self, f: G) -> Liber<F, B>
where
G: Fn(A) -> B + Clone,
{
match self {
Liber::Purus(a) => Liber::Purus(f(a)),
Liber::Suspensus(fa) => {
let mapped = F::map(*fa, |child| child.map(f.clone()));
Liber::Suspensus(Box::new(mapped))
}
}
}
#[inline]
pub fn flat_map<B, G>(self, f: G) -> Liber<F, B>
where
G: Fn(A) -> Liber<F, B> + Clone,
{
match self {
Liber::Purus(a) => f(a),
Liber::Suspensus(fa) => {
let mapped = F::map(*fa, |child| child.flat_map(f.clone()));
Liber::Suspensus(Box::new(mapped))
}
}
}
#[inline]
pub fn lift_f(fa: F::Target<A>) -> Self
where
A: Clone,
{
Liber::Suspensus(Box::new(F::map(fa, Liber::purus)))
}
}
#[cfg(feature = "alloc")]
#[inline]
pub fn plica_liber<F, G, A, NatFn>(nat: NatFn, free: Liber<F, A>) -> G::Target<A>
where
F: FunctorHKT,
G: FunctorHKT + MonadHKT,
NatFn: Fn(F::Target<Liber<F, A>>) -> G::Target<Liber<F, A>> + Clone,
{
plica_liber_impl::<F, G, A, NatFn>(nat, free)
}
#[cfg(feature = "alloc")]
#[inline]
fn plica_liber_impl<F, G, A, NatFn>(nat: NatFn, free: Liber<F, A>) -> G::Target<A>
where
F: FunctorHKT,
G: FunctorHKT + MonadHKT,
NatFn: Fn(F::Target<Liber<F, A>>) -> G::Target<Liber<F, A>> + Clone,
{
match free {
Liber::Purus(a) => G::purus(a),
Liber::Suspensus(fa) => {
let ga: G::Target<Liber<F, A>> = nat(*fa);
G::flat_map(ga, move |next| {
plica_liber_impl::<F, G, A, NatFn>(nat, next)
})
}
}
}
pub trait MonadHKT: FunctorHKT {
fn purus<A>(a: A) -> Self::Target<A>;
fn flat_map<A, B, F>(fa: Self::Target<A>, f: F) -> Self::Target<B>
where
F: FnOnce(A) -> Self::Target<B>;
}
impl MonadHKT for super::nat::OptionFWitness {
fn purus<A>(a: A) -> Option<A> {
Some(a)
}
fn flat_map<A, B, F>(fa: Option<A>, f: F) -> Option<B>
where
F: FnOnce(A) -> Option<B>,
{
fa.and_then(f)
}
}
impl<E: Clone> MonadHKT for super::nat::ResultFWitness<E> {
fn purus<A>(a: A) -> Result<A, E> {
Ok(a)
}
fn flat_map<A, B, F>(fa: Result<A, E>, f: F) -> Result<B, E>
where
F: FnOnce(A) -> Result<B, E>,
{
fa.and_then(f)
}
}
impl MonadHKT for super::nat::IdentitasFWitness {
fn purus<A>(a: A) -> A {
a
}
fn flat_map<A, B, F>(fa: A, f: F) -> B
where
F: FnOnce(A) -> B,
{
f(fa)
}
}
#[cfg(feature = "alloc")]
pub struct LiberWitness<F: FunctorHKT>(core::marker::PhantomData<F>);
#[cfg(feature = "alloc")]
impl<F: FunctorHKT> HKT for LiberWitness<F> {
type Target<A> = Liber<F, A>;
}
#[cfg(feature = "alloc")]
impl<F: FunctorHKT> FunctorHKT for LiberWitness<F> {
fn map<A, B, G>(fa: Liber<F, A>, mut f: G) -> Liber<F, B>
where
G: FnMut(A) -> B,
{
match fa {
Liber::Purus(a) => Liber::Purus(f(a)),
Liber::Suspensus(_) => {
panic!("map on suspended Liber requires Clone")
}
}
}
}
#[cfg(feature = "alloc")]
#[inline]
pub fn itero_liber<F, A, Step>(free: Liber<F, A>, step: Step) -> A
where
F: FunctorHKT,
Step: Fn(F::Target<A>) -> A + Clone,
{
match free {
Liber::Purus(a) => a,
Liber::Suspensus(fa) => {
let mapped = F::map(*fa, |child| itero_liber(child, step.clone()));
step(mapped)
}
}
}
#[cfg(feature = "alloc")]
#[inline]
pub fn join_liber<F: FunctorHKT, A>(nested: Liber<F, Liber<F, A>>) -> Liber<F, A> {
nested.flat_map(|x| x)
}
#[cfg(feature = "alloc")]
#[inline]
pub fn purus_liber<F: FunctorHKT, A>(a: A) -> Liber<F, A> {
Liber::purus(a)
}
#[cfg(test)]
mod tests {
use super::super::nat::OptionFWitness;
use super::*;
#[test]
fn test_purus() {
let free: Liber<OptionFWitness, i32> = Liber::purus(42);
assert!(free.est_purus());
assert!(!free.est_suspensus());
}
#[test]
fn test_map_purus() {
let free: Liber<OptionFWitness, i32> = Liber::purus(42);
let mapped = free.map(|x| x * 2);
match mapped {
Liber::Purus(x) => assert_eq!(x, 84),
_ => panic!("Expected Purus"),
}
}
#[test]
fn test_flat_map_purus() {
let free: Liber<OptionFWitness, i32> = Liber::purus(42);
let chained = free.flat_map(|x| Liber::purus(x + 1));
match chained {
Liber::Purus(x) => assert_eq!(x, 43),
_ => panic!("Expected Purus"),
}
}
#[test]
fn test_chain_operations() {
let free: Liber<OptionFWitness, i32> = Liber::purus(10);
let result = free
.flat_map(|x| Liber::purus(x + 5))
.flat_map(|x| Liber::purus(x * 2))
.map(|x| x - 10);
match result {
Liber::Purus(x) => assert_eq!(x, 20), _ => panic!("Expected Purus"),
}
}
#[test]
fn test_monad_left_identity() {
let a = 42;
let f = |x: i32| Liber::<OptionFWitness, i32>::purus(x * 2);
let left: Liber<OptionFWitness, i32> = Liber::purus(a).flat_map(f);
let right: Liber<OptionFWitness, i32> = f(a);
match (left, right) {
(Liber::Purus(l), Liber::Purus(r)) => assert_eq!(l, r),
_ => panic!("Both should be Purus"),
}
}
#[test]
fn test_monad_right_identity() {
let m: Liber<OptionFWitness, i32> = Liber::purus(42);
let result = m.flat_map(Liber::purus);
match result {
Liber::Purus(r) => assert_eq!(r, 42),
_ => panic!("Should be Purus"),
}
}
#[test]
fn test_join() {
let nested: Liber<OptionFWitness, Liber<OptionFWitness, i32>> =
Liber::purus(Liber::purus(42));
let joined = join_liber(nested);
match joined {
Liber::Purus(x) => assert_eq!(x, 42),
_ => panic!("Expected Purus"),
}
}
}