1#[cfg(feature = "std")]
5use std::rc::Rc;
6
7#[cfg(all(not(feature = "std"), feature = "alloc"))]
8use alloc::rc::Rc;
9
10use core::marker::PhantomData;
11
12use karpal_core::functor::Functor;
13use karpal_core::hkt::HKT;
14
15pub struct Fix<F: HKT>(Rc<F::Of<Fix<F>>>);
25
26impl<F: HKT> Clone for Fix<F> {
27 fn clone(&self) -> Self {
28 Fix(Rc::clone(&self.0))
29 }
30}
31
32impl<F: HKT> Fix<F> {
33 pub fn new(f: F::Of<Fix<F>>) -> Self {
35 Fix(Rc::new(f))
36 }
37
38 pub fn unfix(self) -> F::Of<Fix<F>>
44 where
45 F::Of<Fix<F>>: Clone,
46 {
47 match Rc::try_unwrap(self.0) {
48 Ok(val) => val,
49 Err(rc) => (*rc).clone(),
50 }
51 }
52
53 pub fn unfix_ref(&self) -> &F::Of<Fix<F>> {
55 &self.0
56 }
57}
58
59pub type Mu<F> = Fix<F>;
62
63pub struct FixF<F: HKT>(PhantomData<F>);
65
66impl<F: HKT> HKT for FixF<F> {
67 type Of<T> = Fix<F>;
68}
69
70impl<F: HKT + Functor> Functor for FixF<F> {
71 fn fmap<A, B>(fa: Fix<F>, _f: impl Fn(A) -> B) -> Fix<F> {
72 fa
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use karpal_core::hkt::OptionF;
80
81 fn zero() -> Fix<OptionF> {
82 Fix::new(None)
83 }
84
85 fn succ(n: Fix<OptionF>) -> Fix<OptionF> {
86 Fix::new(Some(n))
87 }
88
89 fn nat(n: u32) -> Fix<OptionF> {
90 let mut result = zero();
91 for _ in 0..n {
92 result = succ(result);
93 }
94 result
95 }
96
97 fn to_u32(n: Fix<OptionF>) -> u32 {
98 match n.unfix() {
99 None => 0,
100 Some(pred) => 1 + to_u32(pred),
101 }
102 }
103
104 #[test]
105 fn fix_unfix_roundtrip() {
106 assert_eq!(to_u32(nat(5)), 5);
107 }
108
109 #[test]
110 fn zero_is_none() {
111 let z = zero();
112 assert!(z.unfix_ref().is_none());
113 }
114
115 #[test]
116 fn succ_is_some() {
117 let one = succ(zero());
118 assert!(one.unfix_ref().is_some());
119 }
120
121 #[test]
122 fn mu_is_fix() {
123 let n: Mu<OptionF> = nat(3);
124 assert_eq!(to_u32(n), 3);
125 }
126
127 #[test]
128 fn clone_fix() {
129 let n = nat(4);
130 let n2 = n.clone();
131 assert_eq!(to_u32(n), 4);
132 assert_eq!(to_u32(n2), 4);
133 }
134}