1use std::rc::Rc;
2
3use crate::optic::Optic;
4use karpal_profunctor::strong::Strong;
5
6pub struct ComposedLens<S, T, X, Y> {
15 getter: Box<dyn Fn(&S) -> X>,
16 setter: Box<dyn Fn(S, Y) -> T>,
17}
18
19pub type SimpleComposedLens<S, X> = ComposedLens<S, S, X, X>;
21
22impl<S, T, X, Y> Optic for ComposedLens<S, T, X, Y> {}
23
24impl<S, T, X, Y> ComposedLens<S, T, X, Y> {
25 pub fn get(&self, s: &S) -> X {
26 (self.getter)(s)
27 }
28
29 pub fn set(&self, s: S, y: Y) -> T {
30 (self.setter)(s, y)
31 }
32}
33
34impl<S: Clone, T, X, Y> ComposedLens<S, T, X, Y> {
35 pub fn over(&self, s: S, f: impl FnOnce(X) -> Y) -> T {
36 let x = (self.getter)(&s);
37 (self.setter)(s, f(x))
38 }
39
40 pub fn then<U, V>(self, inner: Lens<X, Y, U, V>) -> ComposedLens<S, T, U, V>
42 where
43 S: 'static,
44 T: 'static,
45 X: 'static,
46 Y: 'static,
47 U: 'static,
48 V: 'static,
49 {
50 let outer_getter: Rc<dyn Fn(&S) -> X> = self.getter.into();
51 let outer_setter = self.setter;
52 let inner_getter = inner.getter;
53 let inner_setter = inner.setter;
54 let og = Rc::clone(&outer_getter);
55 ComposedLens {
56 getter: Box::new(move |s: &S| inner_getter(&outer_getter(s))),
57 setter: Box::new(move |s: S, v: V| {
58 let x = og(&s);
59 let y = inner_setter(x, v);
60 (outer_setter)(s, y)
61 }),
62 }
63 }
64}
65
66pub struct Lens<S, T, A, B> {
73 getter: fn(&S) -> A,
74 setter: fn(S, B) -> T,
75}
76
77pub type SimpleLens<S, A> = Lens<S, S, A, A>;
79
80impl<S, T, A, B> Optic for Lens<S, T, A, B> {}
81
82impl<S, T, A, B> Lens<S, T, A, B> {
83 pub fn new(getter: fn(&S) -> A, setter: fn(S, B) -> T) -> Self {
84 Self { getter, setter }
85 }
86
87 pub fn get(&self, s: &S) -> A {
88 (self.getter)(s)
89 }
90
91 pub fn set(&self, s: S, b: B) -> T {
92 (self.setter)(s, b)
93 }
94
95 pub fn then<X, Y>(self, inner: Lens<A, B, X, Y>) -> ComposedLens<S, T, X, Y>
97 where
98 S: 'static,
99 T: 'static,
100 A: 'static,
101 B: 'static,
102 X: 'static,
103 Y: 'static,
104 {
105 let outer_getter = self.getter;
106 let outer_setter = self.setter;
107 let inner_getter = inner.getter;
108 let inner_setter = inner.setter;
109 ComposedLens {
110 getter: Box::new(move |s: &S| inner_getter(&outer_getter(s))),
111 setter: Box::new(move |s: S, y: Y| {
112 let a = outer_getter(&s);
113 let b = inner_setter(a, y);
114 outer_setter(s, b)
115 }),
116 }
117 }
118}
119
120impl<S: Clone, T, A, B> Lens<S, T, A, B> {
121 pub fn over(&self, s: S, f: impl FnOnce(A) -> B) -> T {
122 let a = (self.getter)(&s);
123 (self.setter)(s, f(a))
124 }
125
126 pub fn transform<P: Strong>(&self, pab: P::P<A, B>) -> P::P<S, T>
135 where
136 S: 'static,
137 T: 'static,
138 A: 'static,
139 B: 'static,
140 {
141 let getter = self.getter;
142 let setter = self.setter;
143 let first_pab = P::first::<A, B, S>(pab);
144 P::dimap(
145 move |s: S| {
146 let a = getter(&s);
147 (a, s)
148 },
149 move |(b, s)| setter(s, b),
150 first_pab,
151 )
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use karpal_profunctor::FnP;
159 use proptest::prelude::*;
160
161 #[derive(Debug, Clone, PartialEq)]
162 struct Person {
163 name: String,
164 age: u32,
165 }
166
167 fn person_name_lens() -> SimpleLens<Person, String> {
168 Lens::new(|p: &Person| p.name.clone(), |p, name| Person { name, ..p })
169 }
170
171 fn person_age_lens() -> SimpleLens<Person, u32> {
172 Lens::new(|p: &Person| p.age, |p, age| Person { age, ..p })
173 }
174
175 fn sample_person() -> Person {
176 Person {
177 name: "Alice".to_string(),
178 age: 30,
179 }
180 }
181
182 #[test]
183 fn lens_get() {
184 let lens = person_name_lens();
185 assert_eq!(lens.get(&sample_person()), "Alice");
186 }
187
188 #[test]
189 fn lens_set() {
190 let lens = person_name_lens();
191 let updated = lens.set(sample_person(), "Bob".to_string());
192 assert_eq!(updated.name, "Bob");
193 assert_eq!(updated.age, 30);
194 }
195
196 #[test]
197 fn lens_over() {
198 let lens = person_age_lens();
199 let updated = lens.over(sample_person(), |age| age + 1);
200 assert_eq!(updated.age, 31);
201 assert_eq!(updated.name, "Alice");
202 }
203
204 #[test]
207 fn law_get_put() {
208 let lens = person_name_lens();
209 let p = sample_person();
210 let result = lens.set(p.clone(), lens.get(&p));
211 assert_eq!(result, p);
212 }
213
214 #[test]
216 fn law_put_get() {
217 let lens = person_name_lens();
218 let result = lens.set(sample_person(), "Bob".to_string());
219 assert_eq!(lens.get(&result), "Bob");
220 }
221
222 #[test]
224 fn law_put_put() {
225 let lens = person_name_lens();
226 let p = sample_person();
227 let left = lens.set(
228 lens.set(p.clone(), "Bob".to_string()),
229 "Charlie".to_string(),
230 );
231 let right = lens.set(p, "Charlie".to_string());
232 assert_eq!(left, right);
233 }
234
235 #[test]
237 fn lens_transform_fnp() {
238 let lens = person_age_lens();
239 let increment: Box<dyn Fn(u32) -> u32> = Box::new(|age| age + 1);
240 let transform_fn = lens.transform::<FnP>(increment);
241 let result = transform_fn(sample_person());
242 assert_eq!(result.age, 31);
243 assert_eq!(result.name, "Alice");
244 }
245
246 #[test]
247 fn lens_transform_fnp_name() {
248 let lens = person_name_lens();
249 let upper: Box<dyn Fn(String) -> String> = Box::new(|s| s.to_uppercase());
250 let transform_fn = lens.transform::<FnP>(upper);
251 let result = transform_fn(sample_person());
252 assert_eq!(result.name, "ALICE");
253 assert_eq!(result.age, 30);
254 }
255
256 #[derive(Debug, Clone, PartialEq)]
259 struct Address {
260 street: String,
261 city: String,
262 }
263
264 #[derive(Debug, Clone, PartialEq)]
265 struct Company {
266 name: String,
267 ceo: Person,
268 }
269
270 fn company_ceo_lens() -> SimpleLens<Company, Person> {
271 Lens::new(|c: &Company| c.ceo.clone(), |c, ceo| Company { ceo, ..c })
272 }
273
274 fn address_city_lens() -> SimpleLens<Address, String> {
275 Lens::new(
276 |a: &Address| a.city.clone(),
277 |a, city| Address { city, ..a },
278 )
279 }
280
281 fn address_street_lens() -> SimpleLens<Address, String> {
282 Lens::new(
283 |a: &Address| a.street.clone(),
284 |a, street| Address { street, ..a },
285 )
286 }
287
288 fn sample_company() -> Company {
289 Company {
290 name: "Acme".to_string(),
291 ceo: sample_person(),
292 }
293 }
294
295 #[test]
297 fn composed_get() {
298 let lens = company_ceo_lens().then(person_name_lens());
299 assert_eq!(lens.get(&sample_company()), "Alice");
300 }
301
302 #[test]
303 fn composed_set() {
304 let lens = company_ceo_lens().then(person_name_lens());
305 let updated = lens.set(sample_company(), "Bob".to_string());
306 assert_eq!(updated.ceo.name, "Bob");
307 assert_eq!(updated.ceo.age, 30);
308 assert_eq!(updated.name, "Acme");
309 }
310
311 #[test]
312 fn composed_over() {
313 let lens = company_ceo_lens().then(person_age_lens());
314 let updated = lens.over(sample_company(), |age| age + 1);
315 assert_eq!(updated.ceo.age, 31);
316 assert_eq!(updated.ceo.name, "Alice");
317 }
318
319 #[derive(Debug, Clone, PartialEq)]
322 struct PersonWithAddr {
323 name: String,
324 addr: Address,
325 }
326
327 #[derive(Debug, Clone, PartialEq)]
328 struct Org {
329 title: String,
330 lead: PersonWithAddr,
331 }
332
333 fn org_lead_lens() -> SimpleLens<Org, PersonWithAddr> {
334 Lens::new(|o: &Org| o.lead.clone(), |o, lead| Org { lead, ..o })
335 }
336
337 fn pwa_addr_lens() -> SimpleLens<PersonWithAddr, Address> {
338 Lens::new(
339 |p: &PersonWithAddr| p.addr.clone(),
340 |p, addr| PersonWithAddr { addr, ..p },
341 )
342 }
343
344 fn sample_org() -> Org {
345 Org {
346 title: "R&D".to_string(),
347 lead: PersonWithAddr {
348 name: "Alice".to_string(),
349 addr: Address {
350 street: "123 Main St".to_string(),
351 city: "Springfield".to_string(),
352 },
353 },
354 }
355 }
356
357 #[test]
358 fn three_deep_get() {
359 let lens = org_lead_lens()
360 .then(pwa_addr_lens())
361 .then(address_city_lens());
362 assert_eq!(lens.get(&sample_org()), "Springfield");
363 }
364
365 #[test]
366 fn three_deep_set() {
367 let lens = org_lead_lens()
368 .then(pwa_addr_lens())
369 .then(address_city_lens());
370 let updated = lens.set(sample_org(), "Shelbyville".to_string());
371 assert_eq!(updated.lead.addr.city, "Shelbyville");
372 assert_eq!(updated.lead.addr.street, "123 Main St");
373 assert_eq!(updated.lead.name, "Alice");
374 }
375
376 #[test]
377 fn three_deep_over() {
378 let lens = org_lead_lens()
379 .then(pwa_addr_lens())
380 .then(address_street_lens());
381 let updated = lens.over(sample_org(), |s| s.to_uppercase());
382 assert_eq!(updated.lead.addr.street, "123 MAIN ST");
383 }
384
385 proptest! {
389 #[test]
391 fn composed_law_get_put(name in "[a-z]{1,8}", co_name in "[a-z]{1,8}", age in 0u32..1000) {
392 let lens = company_ceo_lens().then(person_age_lens());
393 let c = Company {
394 name: co_name,
395 ceo: Person { name, age },
396 };
397 let result = lens.set(c.clone(), lens.get(&c));
398 prop_assert_eq!(result, c);
399 }
400
401 #[test]
403 fn composed_law_put_get(name in "[a-z]{1,8}", co_name in "[a-z]{1,8}", age in 0u32..1000, new_age in 0u32..1000) {
404 let lens = company_ceo_lens().then(person_age_lens());
405 let c = Company {
406 name: co_name,
407 ceo: Person { name, age },
408 };
409 let result = lens.set(c, new_age);
410 prop_assert_eq!(lens.get(&result), new_age);
411 }
412
413 #[test]
415 fn composed_law_put_put(name in "[a-z]{1,8}", co_name in "[a-z]{1,8}", age in 0u32..1000, b1 in 0u32..1000, b2 in 0u32..1000) {
416 let lens = company_ceo_lens().then(person_age_lens());
417 let c = Company {
418 name: co_name,
419 ceo: Person { name, age },
420 };
421 let left = lens.set(lens.set(c.clone(), b1), b2);
422 let right = lens.set(c, b2);
423 prop_assert_eq!(left, right);
424 }
425 }
426
427 #[test]
429 fn profunctor_composition_equivalence() {
430 let outer = company_ceo_lens();
431 let inner = person_age_lens();
432 let composed = company_ceo_lens().then(person_age_lens());
433
434 let increment: Box<dyn Fn(u32) -> u32> = Box::new(|age| age + 1);
435 let transform_fn = outer.transform::<FnP>(inner.transform::<FnP>(increment));
436
437 let c = sample_company();
438 let via_profunctor = transform_fn(c.clone());
439 let via_composed = composed.over(c, |age| age + 1);
440 assert_eq!(via_profunctor, via_composed);
441 }
442}