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