1use core::marker::PhantomData;
2
3use karpal_core::hkt::HKT;
4
5use crate::classes::{ApplicativeSt, ChainSt, FunctorSt};
6use crate::trans::MonadTrans;
7
8#[cfg(all(not(feature = "std"), feature = "alloc"))]
9use alloc::rc::Rc;
10#[cfg(feature = "std")]
11use std::rc::Rc;
12
13pub struct ReaderTF<E, M>(PhantomData<(E, M)>);
19
20impl<E: 'static, M: HKT + 'static> HKT for ReaderTF<E, M> {
21 type Of<A> = Box<dyn Fn(E) -> M::Of<A>>;
22}
23
24impl<E: 'static, M: FunctorSt + 'static> MonadTrans<M> for ReaderTF<E, M> {
25 fn lift<A: 'static>(ma: M::Of<A>) -> Box<dyn Fn(E) -> M::Of<A>>
26 where
27 M::Of<A>: Clone,
28 {
29 Box::new(move |_| ma.clone())
30 }
31}
32
33pub fn reader_t_pure<E: 'static, M: ApplicativeSt + 'static, A: Clone + 'static>(
35 a: A,
36) -> Box<dyn Fn(E) -> M::Of<A>> {
37 Box::new(move |_| M::pure_st(a.clone()))
38}
39
40pub fn reader_t_fmap<E: 'static, M: FunctorSt + 'static, A: 'static, B: 'static>(
42 fa: Box<dyn Fn(E) -> M::Of<A>>,
43 f: impl Fn(A) -> B + 'static,
44) -> Box<dyn Fn(E) -> M::Of<B>> {
45 let f_rc = Rc::new(f);
46 Box::new(move |e| {
47 let ma = fa(e);
48 let f_inner = f_rc.clone();
49 M::fmap_st(ma, move |a| f_inner(a))
50 })
51}
52
53pub fn reader_t_chain<E: Clone + 'static, M: ChainSt + 'static, A: 'static, B: 'static>(
57 fa: Box<dyn Fn(E) -> M::Of<A>>,
58 f: impl Fn(A) -> Box<dyn Fn(E) -> M::Of<B>> + 'static,
59) -> Box<dyn Fn(E) -> M::Of<B>> {
60 let f_rc = Rc::new(f);
61 Box::new(move |e: E| {
62 let ma = fa(e.clone());
63 let e2 = e;
64 let f_inner = f_rc.clone();
65 M::chain_st(ma, move |a| {
66 let reader_b = f_inner(a);
67 reader_b(e2.clone())
68 })
69 })
70}
71
72pub fn reader_t_ask<E: Clone + 'static, M: ApplicativeSt + 'static>() -> Box<dyn Fn(E) -> M::Of<E>>
74{
75 Box::new(|e| M::pure_st(e))
76}
77
78pub fn reader_t_local<E: 'static, M: HKT + 'static, A: 'static>(
80 f: impl Fn(E) -> E + 'static,
81 reader: Box<dyn Fn(E) -> M::Of<A>>,
82) -> Box<dyn Fn(E) -> M::Of<A>> {
83 Box::new(move |e| reader(f(e)))
84}
85
86pub fn reader_t_reader<E: 'static, M: ApplicativeSt + 'static, A: 'static>(
88 f: impl Fn(E) -> A + 'static,
89) -> Box<dyn Fn(E) -> M::Of<A>> {
90 Box::new(move |e| M::pure_st(f(e)))
91}
92
93pub fn reader_t_run<E, M: HKT, A>(reader: &dyn Fn(E) -> M::Of<A>, env: E) -> M::Of<A> {
95 reader(env)
96}
97
98impl<E: 'static, M: FunctorSt + 'static> FunctorSt for ReaderTF<E, M> {
104 fn fmap_st<A: 'static, B: 'static>(
105 fa: Box<dyn Fn(E) -> M::Of<A>>,
106 f: impl Fn(A) -> B + 'static,
107 ) -> Box<dyn Fn(E) -> M::Of<B>> {
108 reader_t_fmap::<E, M, A, B>(fa, f)
109 }
110}
111
112impl<E: Clone + 'static, M: ChainSt + 'static> ChainSt for ReaderTF<E, M> {
113 fn chain_st<A: 'static, B: 'static>(
114 fa: Box<dyn Fn(E) -> M::Of<A>>,
115 f: impl Fn(A) -> Box<dyn Fn(E) -> M::Of<B>> + 'static,
116 ) -> Box<dyn Fn(E) -> M::Of<B>> {
117 reader_t_chain::<E, M, A, B>(fa, f)
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use karpal_core::hkt::OptionF;
125
126 #[test]
127 fn reader_t_pure_test() {
128 let r = reader_t_pure::<i32, OptionF, _>(42);
129 assert_eq!(r(0), Some(42));
130 assert_eq!(r(999), Some(42));
131 }
132
133 #[test]
134 fn reader_t_ask_test() {
135 let r = reader_t_ask::<i32, OptionF>();
136 assert_eq!(r(42), Some(42));
137 }
138
139 #[test]
140 fn reader_t_fmap_test() {
141 let r = reader_t_ask::<i32, OptionF>();
142 let mapped = reader_t_fmap::<i32, OptionF, _, _>(r, |x| x * 2);
143 assert_eq!(mapped(5), Some(10));
144 }
145
146 #[test]
147 fn reader_t_chain_test() {
148 let r = reader_t_ask::<i32, OptionF>();
149 let chained =
150 reader_t_chain::<i32, OptionF, _, _>(r, |x| reader_t_pure::<i32, OptionF, _>(x + 10));
151 assert_eq!(chained(5), Some(15));
152 }
153
154 #[test]
155 fn reader_t_chain_shares_env() {
156 let r = reader_t_chain::<i32, OptionF, _, _>(reader_t_ask::<i32, OptionF>(), |x| {
157 let x_captured = x;
158 reader_t_fmap::<i32, OptionF, _, _>(reader_t_ask::<i32, OptionF>(), move |e| {
159 e + x_captured
160 })
161 });
162 assert_eq!(r(10), Some(20));
163 }
164
165 #[test]
166 fn reader_t_local_test() {
167 let r = reader_t_ask::<i32, OptionF>();
168 let localized = reader_t_local::<i32, OptionF, i32>(|e| e + 100, r);
169 assert_eq!(localized(5), Some(105));
170 }
171
172 #[test]
173 fn reader_t_reader_test() {
174 let r = reader_t_reader::<String, OptionF, _>(|s: String| s.len());
175 assert_eq!(r("hello".to_string()), Some(5));
176 }
177
178 #[test]
179 fn reader_t_lift_test() {
180 let lifted = ReaderTF::<i32, OptionF>::lift(Some(42));
181 assert_eq!(lifted(999), Some(42));
182 }
183
184 #[test]
185 fn reader_t_lift_none() {
186 let lifted = ReaderTF::<i32, OptionF>::lift(None::<i32>);
187 assert_eq!(lifted(999), None);
188 }
189
190 #[test]
191 fn reader_t_run_test() {
192 let r = reader_t_ask::<i32, OptionF>();
193 assert_eq!(reader_t_run::<i32, OptionF, i32>(&*r, 42), Some(42));
194 }
195
196 #[test]
197 fn reader_t_functor_st_trait() {
198 let r = reader_t_pure::<i32, OptionF, _>(5);
199 let mapped = ReaderTF::<i32, OptionF>::fmap_st(r, |x| x + 1);
200 assert_eq!(mapped(0), Some(6));
201 }
202
203 #[test]
204 fn reader_t_chain_st_trait() {
205 let r = reader_t_pure::<i32, OptionF, _>(5);
206 let chained =
207 ReaderTF::<i32, OptionF>::chain_st(r, |x| reader_t_pure::<i32, OptionF, _>(x + 10));
208 assert_eq!(chained(0), Some(15));
209 }
210}
211
212#[cfg(test)]
213mod law_tests {
214 use super::*;
215 use karpal_core::hkt::OptionF;
216 use proptest::prelude::*;
217
218 proptest! {
219 #[test]
220 fn reader_t_monad_left_identity(a in -100i32..100, e in -100i32..100) {
221 let f = |x: i32| -> Box<dyn Fn(i32) -> Option<i32>> {
222 reader_t_pure::<i32, OptionF, _>(x + 1)
223 };
224 let left = reader_t_chain::<i32, OptionF, _, _>(
225 reader_t_pure::<i32, OptionF, _>(a),
226 f,
227 );
228 let right = f(a);
229 prop_assert_eq!(left(e), right(e));
230 }
231
232 #[test]
233 fn reader_t_monad_right_identity(a in -100i32..100, e in -100i32..100) {
234 let m = reader_t_pure::<i32, OptionF, _>(a);
235 let left = reader_t_chain::<i32, OptionF, _, _>(
236 reader_t_pure::<i32, OptionF, _>(a),
237 |x| reader_t_pure::<i32, OptionF, _>(x),
238 );
239 prop_assert_eq!(left(e), m(e));
240 }
241
242 #[test]
243 fn reader_t_functor_identity(a in -100i32..100, e in -100i32..100) {
244 let m = reader_t_pure::<i32, OptionF, _>(a);
245 let mapped = reader_t_fmap::<i32, OptionF, _, _>(
246 reader_t_pure::<i32, OptionF, _>(a),
247 |x| x,
248 );
249 prop_assert_eq!(mapped(e), m(e));
250 }
251
252 #[test]
253 fn reader_t_lift_pure(a in any::<i32>(), e in any::<i32>()) {
254 let lift_pure = ReaderTF::<i32, OptionF>::lift(Some(a));
255 let pure_a = reader_t_pure::<i32, OptionF, _>(a);
256 prop_assert_eq!(lift_pure(e), pure_a(e));
257 }
258 }
259}