#[cfg(feature = "std")]
use std::rc::Rc;
#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::rc::Rc;
use core::marker::PhantomData;
use karpal_core::functor::Functor;
use karpal_core::hkt::HKT;
pub struct Fix<F: HKT>(Rc<F::Of<Fix<F>>>);
impl<F: HKT> Clone for Fix<F> {
fn clone(&self) -> Self {
Fix(Rc::clone(&self.0))
}
}
impl<F: HKT> Fix<F> {
pub fn new(f: F::Of<Fix<F>>) -> Self {
Fix(Rc::new(f))
}
pub fn unfix(self) -> F::Of<Fix<F>>
where
F::Of<Fix<F>>: Clone,
{
match Rc::try_unwrap(self.0) {
Ok(val) => val,
Err(rc) => (*rc).clone(),
}
}
pub fn unfix_ref(&self) -> &F::Of<Fix<F>> {
&self.0
}
}
pub type Mu<F> = Fix<F>;
pub struct FixF<F: HKT>(PhantomData<F>);
impl<F: HKT> HKT for FixF<F> {
type Of<T> = Fix<F>;
}
impl<F: HKT + Functor> Functor for FixF<F> {
fn fmap<A, B>(fa: Fix<F>, _f: impl Fn(A) -> B) -> Fix<F> {
fa
}
}
#[cfg(test)]
mod tests {
use super::*;
use karpal_core::hkt::OptionF;
fn zero() -> Fix<OptionF> {
Fix::new(None)
}
fn succ(n: Fix<OptionF>) -> Fix<OptionF> {
Fix::new(Some(n))
}
fn nat(n: u32) -> Fix<OptionF> {
let mut result = zero();
for _ in 0..n {
result = succ(result);
}
result
}
fn to_u32(n: Fix<OptionF>) -> u32 {
match n.unfix() {
None => 0,
Some(pred) => 1 + to_u32(pred),
}
}
#[test]
fn fix_unfix_roundtrip() {
assert_eq!(to_u32(nat(5)), 5);
}
#[test]
fn zero_is_none() {
let z = zero();
assert!(z.unfix_ref().is_none());
}
#[test]
fn succ_is_some() {
let one = succ(zero());
assert!(one.unfix_ref().is_some());
}
#[test]
fn mu_is_fix() {
let n: Mu<OptionF> = nat(3);
assert_eq!(to_u32(n), 3);
}
#[test]
fn clone_fix() {
let n = nat(4);
let n2 = n.clone();
assert_eq!(to_u32(n), 4);
assert_eq!(to_u32(n2), 4);
}
}