1#[cfg(feature = "std")]
2use std::boxed::Box;
3
4#[cfg(all(not(feature = "std"), feature = "alloc"))]
5use alloc::boxed::Box;
6
7use core::marker::PhantomData;
8
9use karpal_core::applicative::Applicative;
10use karpal_core::chain::Chain;
11use karpal_core::functor::Functor;
12use karpal_core::hkt::HKT;
13use karpal_core::natural::NaturalTransformation;
14
15pub enum Free<F: HKT, A> {
27 Pure(A),
29 Roll(Box<F::Of<Free<F, A>>>),
31}
32
33impl<F: HKT, A> Free<F, A> {
34 pub fn pure(a: A) -> Self {
36 Free::Pure(a)
37 }
38}
39
40impl<F: HKT + Functor, A> Free<F, A> {
41 pub fn lift_f(fa: F::Of<A>) -> Self {
43 Free::Roll(Box::new(F::fmap(fa, Free::Pure)))
44 }
45
46 pub fn fmap<B>(self, f: impl Fn(A) -> B) -> Free<F, B> {
48 self.fmap_inner(&f)
49 }
50
51 fn fmap_inner<B>(self, f: &dyn Fn(A) -> B) -> Free<F, B> {
52 match self {
53 Free::Pure(a) => Free::Pure(f(a)),
54 Free::Roll(ff) => Free::Roll(Box::new(F::fmap(*ff, |child| child.fmap_inner(f)))),
55 }
56 }
57
58 pub fn chain<B>(self, f: impl Fn(A) -> Free<F, B>) -> Free<F, B> {
61 self.chain_inner(&f)
62 }
63
64 fn chain_inner<B>(self, f: &dyn Fn(A) -> Free<F, B>) -> Free<F, B> {
65 match self {
66 Free::Pure(a) => f(a),
67 Free::Roll(ff) => Free::Roll(Box::new(F::fmap(*ff, |child| child.chain_inner(f)))),
68 }
69 }
70
71 pub fn fold_map<M, NT>(self) -> M::Of<A>
77 where
78 M: Applicative + Chain,
79 NT: NaturalTransformation<F, M>,
80 {
81 match self {
82 Free::Pure(a) => M::pure(a),
83 Free::Roll(ff) => {
84 let mapped = F::fmap(*ff, |child| child.fold_map::<M, NT>());
85 let m_ma: M::Of<M::Of<A>> = NT::transform(mapped);
86 M::chain(m_ma, |x| x)
87 }
88 }
89 }
90}
91
92pub struct FreeF<F: HKT>(PhantomData<F>);
94
95impl<F: HKT> HKT for FreeF<F> {
96 type Of<T> = Free<F, T>;
97}
98
99impl<F: HKT + Functor> Functor for FreeF<F> {
100 fn fmap<A, B>(fa: Free<F, A>, f: impl Fn(A) -> B) -> Free<F, B> {
101 fa.fmap(f)
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use karpal_core::hkt::OptionF;
109
110 #[test]
111 fn pure_value() {
112 let free = Free::<OptionF, i32>::pure(42);
113 match free {
114 Free::Pure(v) => assert_eq!(v, 42),
115 Free::Roll(_) => panic!("expected Pure"),
116 }
117 }
118
119 #[test]
120 fn lift_f_some() {
121 let free = Free::<OptionF, i32>::lift_f(Some(1));
122 match free {
123 Free::Roll(ff) => match *ff {
124 Some(Free::Pure(v)) => assert_eq!(v, 1),
125 _ => panic!("expected Some(Pure(1))"),
126 },
127 Free::Pure(_) => panic!("expected Roll"),
128 }
129 }
130
131 #[test]
132 fn fmap_pure() {
133 let free = Free::<OptionF, i32>::pure(2).fmap(|x| x * 3);
134 match free {
135 Free::Pure(v) => assert_eq!(v, 6),
136 Free::Roll(_) => panic!("expected Pure"),
137 }
138 }
139
140 #[test]
141 fn fmap_roll() {
142 let free = Free::<OptionF, i32>::lift_f(Some(5)).fmap(|x| x + 10);
143 match free {
144 Free::Roll(ff) => match *ff {
145 Some(Free::Pure(v)) => assert_eq!(v, 15),
146 _ => panic!("expected Some(Pure(15))"),
147 },
148 Free::Pure(_) => panic!("expected Roll"),
149 }
150 }
151
152 #[test]
153 fn chain_pure() {
154 let free = Free::<OptionF, i32>::pure(1).chain(|x| Free::pure(x + 1));
155 match free {
156 Free::Pure(v) => assert_eq!(v, 2),
157 Free::Roll(_) => panic!("expected Pure"),
158 }
159 }
160
161 #[test]
162 fn chain_roll() {
163 let free = Free::<OptionF, i32>::lift_f(Some(10)).chain(|x| Free::pure(x * 2));
164 match free {
170 Free::Roll(ff) => match *ff {
171 Some(Free::Pure(v)) => assert_eq!(v, 20),
172 _ => panic!("expected Some(Pure(20))"),
173 },
174 Free::Pure(_) => panic!("expected Roll"),
175 }
176 }
177
178 #[test]
179 fn chain_associativity() {
180 let _m = Free::<OptionF, i32>::pure(5);
181 let _f = |x: i32| Free::<OptionF, i32>::pure(x + 1);
182 let _g = |x: i32| Free::<OptionF, i32>::pure(x * 2);
183
184 let left = Free::<OptionF, i32>::pure(5)
186 .chain(|x| Free::pure(x + 1))
187 .chain(|x| Free::pure(x * 2));
188
189 let right = Free::<OptionF, i32>::pure(5)
191 .chain(|x| Free::<OptionF, i32>::pure(x + 1).chain(|y| Free::pure(y * 2)));
192
193 match (left, right) {
194 (Free::Pure(l), Free::Pure(r)) => assert_eq!(l, r),
195 _ => panic!("expected both Pure"),
196 }
197 }
198
199 struct OptionId;
201 impl NaturalTransformation<OptionF, OptionF> for OptionId {
202 fn transform<A>(fa: Option<A>) -> Option<A> {
203 fa
204 }
205 }
206
207 #[test]
208 fn fold_map_pure() {
209 let free = Free::<OptionF, i32>::pure(42);
210 let result = free.fold_map::<OptionF, OptionId>();
211 assert_eq!(result, Some(42));
212 }
213
214 #[test]
215 fn fold_map_roll() {
216 let free = Free::<OptionF, i32>::lift_f(Some(10));
217 let result = free.fold_map::<OptionF, OptionId>();
218 assert_eq!(result, Some(10));
219 }
220
221 #[test]
222 fn fold_map_chain_then_interpret() {
223 let free = Free::<OptionF, i32>::lift_f(Some(3)).chain(|x| Free::lift_f(Some(x * 10)));
224 let result = free.fold_map::<OptionF, OptionId>();
225 assert_eq!(result, Some(30));
226 }
227
228 #[test]
229 fn functor_impl_works() {
230 let free = Free::<OptionF, i32>::pure(5);
231 let result = <FreeF<OptionF> as Functor>::fmap(free, |x| x + 10);
232 match result {
233 Free::Pure(v) => assert_eq!(v, 15),
234 Free::Roll(_) => panic!("expected Pure"),
235 }
236 }
237}
238
239#[cfg(test)]
240mod law_tests {
241 use super::*;
242 use karpal_core::hkt::OptionF;
243 use proptest::prelude::*;
244
245 fn extract_pure<F: HKT, A>(free: Free<F, A>) -> Option<A> {
247 match free {
248 Free::Pure(a) => Some(a),
249 Free::Roll(_) => None,
250 }
251 }
252
253 proptest! {
254 #[test]
256 fn functor_identity(x in any::<i32>()) {
257 let free = Free::<OptionF, i32>::pure(x);
258 let result = free.fmap(|a| a);
259 prop_assert_eq!(extract_pure(result), Some(x));
260 }
261
262 #[test]
264 fn functor_composition(x in any::<i32>()) {
265 let f = |a: i32| a.wrapping_add(1);
266 let g = |a: i32| a.wrapping_mul(2);
267
268 let left = Free::<OptionF, i32>::pure(x).fmap(|a| g(f(a)));
269 let right = Free::<OptionF, i32>::pure(x).fmap(f).fmap(g);
270 prop_assert_eq!(extract_pure(left), extract_pure(right));
271 }
272
273 #[test]
275 fn monad_left_identity(x in any::<i32>()) {
276 let f = |a: i32| Free::<OptionF, i32>::pure(a.wrapping_mul(2));
277 let left = Free::<OptionF, i32>::pure(x).chain(&f);
278 let right = f(x);
279 prop_assert_eq!(extract_pure(left), extract_pure(right));
280 }
281
282 #[test]
284 fn monad_right_identity(x in any::<i32>()) {
285 let m = Free::<OptionF, i32>::pure(x);
286 let result = m.chain(Free::pure);
287 prop_assert_eq!(extract_pure(result), Some(x));
288 }
289 }
290}