1use std::rc::Rc;
2
3use crate::fold::Fold;
4use crate::getter::Getter;
5use crate::optic::Optic;
6use crate::setter::Setter;
7use crate::traversal::Traversal;
8use karpal_profunctor::strong::Strong;
9
10pub struct ComposedLens<S, T, X, Y> {
19 getter: Box<dyn Fn(&S) -> X>,
20 setter: Box<dyn Fn(S, Y) -> T>,
21}
22
23pub type SimpleComposedLens<S, X> = ComposedLens<S, S, X, X>;
25
26impl<S, T, X, Y> Optic for ComposedLens<S, T, X, Y> {}
27
28impl<S, T, X, Y> ComposedLens<S, T, X, Y> {
29 pub fn from_fns(getter: Box<dyn Fn(&S) -> X>, setter: Box<dyn Fn(S, Y) -> T>) -> Self {
31 Self { getter, setter }
32 }
33
34 pub fn get(&self, s: &S) -> X {
35 (self.getter)(s)
36 }
37
38 pub fn set(&self, s: S, y: Y) -> T {
39 (self.setter)(s, y)
40 }
41}
42
43impl<S: Clone, T, X, Y> ComposedLens<S, T, X, Y> {
44 pub fn over(&self, s: S, f: impl FnOnce(X) -> Y) -> T {
45 let x = (self.getter)(&s);
46 (self.setter)(s, f(x))
47 }
48
49 pub fn then<U, V>(self, inner: Lens<X, Y, U, V>) -> ComposedLens<S, T, U, V>
51 where
52 S: 'static,
53 T: 'static,
54 X: 'static,
55 Y: 'static,
56 U: 'static,
57 V: 'static,
58 {
59 let outer_getter: Rc<dyn Fn(&S) -> X> = self.getter.into();
60 let outer_setter = self.setter;
61 let inner_getter = inner.getter;
62 let inner_setter = inner.setter;
63 let og = Rc::clone(&outer_getter);
64 ComposedLens {
65 getter: Box::new(move |s: &S| inner_getter(&outer_getter(s))),
66 setter: Box::new(move |s: S, v: V| {
67 let x = og(&s);
68 let y = inner_setter(x, v);
69 (outer_setter)(s, y)
70 }),
71 }
72 }
73}
74
75pub struct Lens<S, T, A, B> {
82 getter: fn(&S) -> A,
83 setter: fn(S, B) -> T,
84}
85
86pub type SimpleLens<S, A> = Lens<S, S, A, A>;
88
89impl<S, T, A, B> Optic for Lens<S, T, A, B> {}
90
91impl<S, T, A, B> Lens<S, T, A, B> {
92 pub fn new(getter: fn(&S) -> A, setter: fn(S, B) -> T) -> Self {
93 Self { getter, setter }
94 }
95
96 pub fn get(&self, s: &S) -> A {
97 (self.getter)(s)
98 }
99
100 pub fn set(&self, s: S, b: B) -> T {
101 (self.setter)(s, b)
102 }
103
104 pub fn then<X, Y>(self, inner: Lens<A, B, X, Y>) -> ComposedLens<S, T, X, Y>
106 where
107 S: 'static,
108 T: 'static,
109 A: 'static,
110 B: 'static,
111 X: 'static,
112 Y: 'static,
113 {
114 let outer_getter = self.getter;
115 let outer_setter = self.setter;
116 let inner_getter = inner.getter;
117 let inner_setter = inner.setter;
118 ComposedLens {
119 getter: Box::new(move |s: &S| inner_getter(&outer_getter(s))),
120 setter: Box::new(move |s: S, y: Y| {
121 let a = outer_getter(&s);
122 let b = inner_setter(a, y);
123 outer_setter(s, b)
124 }),
125 }
126 }
127}
128
129impl<S, T, A, B> Lens<S, T, A, B> {
130 pub fn to_getter(&self) -> Getter<S, A> {
132 Getter::new(self.getter)
133 }
134
135 pub fn to_setter(&self) -> Setter<S, T, A, B>
137 where
138 S: 'static,
139 T: 'static,
140 A: 'static,
141 B: 'static,
142 {
143 let getter = self.getter;
144 let setter = self.setter;
145 Setter::new(move |s: S, f: &dyn Fn(A) -> B| {
146 let a = getter(&s);
147 setter(s, f(a))
148 })
149 }
150
151 pub fn to_traversal(&self) -> Traversal<S, T, A, B>
153 where
154 S: 'static,
155 T: 'static,
156 A: 'static,
157 B: 'static,
158 {
159 let getter = self.getter;
160 let setter = self.setter;
161 Traversal::new(
162 move |s| vec![getter(s)],
163 move |s, f| {
164 let a = getter(&s);
165 setter(s, f(a))
166 },
167 )
168 }
169
170 pub fn to_fold(&self) -> Fold<S, A>
172 where
173 S: 'static,
174 A: 'static,
175 {
176 let getter = self.getter;
177 Fold::new(move |s| vec![getter(s)])
178 }
179}
180
181impl<S: Clone, T, A, B> Lens<S, T, A, B> {
182 pub fn over(&self, s: S, f: impl FnOnce(A) -> B) -> T {
183 let a = (self.getter)(&s);
184 (self.setter)(s, f(a))
185 }
186
187 pub fn transform<P: Strong>(&self, pab: P::P<A, B>) -> P::P<S, T>
196 where
197 S: 'static,
198 T: 'static,
199 A: 'static,
200 B: 'static,
201 {
202 let getter = self.getter;
203 let setter = self.setter;
204 let first_pab = P::first::<A, B, S>(pab);
205 P::dimap(
206 move |s: S| {
207 let a = getter(&s);
208 (a, s)
209 },
210 move |(b, s)| setter(s, b),
211 first_pab,
212 )
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use karpal_profunctor::FnP;
220 use proptest::prelude::*;
221
222 #[derive(Debug, Clone, PartialEq)]
223 struct Person {
224 name: String,
225 age: u32,
226 }
227
228 fn person_name_lens() -> SimpleLens<Person, String> {
229 Lens::new(|p: &Person| p.name.clone(), |p, name| Person { name, ..p })
230 }
231
232 fn person_age_lens() -> SimpleLens<Person, u32> {
233 Lens::new(|p: &Person| p.age, |p, age| Person { age, ..p })
234 }
235
236 fn sample_person() -> Person {
237 Person {
238 name: "Alice".to_string(),
239 age: 30,
240 }
241 }
242
243 #[test]
244 fn lens_get() {
245 let lens = person_name_lens();
246 assert_eq!(lens.get(&sample_person()), "Alice");
247 }
248
249 #[test]
250 fn lens_set() {
251 let lens = person_name_lens();
252 let updated = lens.set(sample_person(), "Bob".to_string());
253 assert_eq!(updated.name, "Bob");
254 assert_eq!(updated.age, 30);
255 }
256
257 #[test]
258 fn lens_over() {
259 let lens = person_age_lens();
260 let updated = lens.over(sample_person(), |age| age + 1);
261 assert_eq!(updated.age, 31);
262 assert_eq!(updated.name, "Alice");
263 }
264
265 #[test]
268 fn law_get_put() {
269 let lens = person_name_lens();
270 let p = sample_person();
271 let result = lens.set(p.clone(), lens.get(&p));
272 assert_eq!(result, p);
273 }
274
275 #[test]
277 fn law_put_get() {
278 let lens = person_name_lens();
279 let result = lens.set(sample_person(), "Bob".to_string());
280 assert_eq!(lens.get(&result), "Bob");
281 }
282
283 #[test]
285 fn law_put_put() {
286 let lens = person_name_lens();
287 let p = sample_person();
288 let left = lens.set(
289 lens.set(p.clone(), "Bob".to_string()),
290 "Charlie".to_string(),
291 );
292 let right = lens.set(p, "Charlie".to_string());
293 assert_eq!(left, right);
294 }
295
296 #[test]
298 fn lens_transform_fnp() {
299 let lens = person_age_lens();
300 let increment: Box<dyn Fn(u32) -> u32> = Box::new(|age| age + 1);
301 let transform_fn = lens.transform::<FnP>(increment);
302 let result = transform_fn(sample_person());
303 assert_eq!(result.age, 31);
304 assert_eq!(result.name, "Alice");
305 }
306
307 #[test]
308 fn lens_transform_fnp_name() {
309 let lens = person_name_lens();
310 let upper: Box<dyn Fn(String) -> String> = Box::new(|s| s.to_uppercase());
311 let transform_fn = lens.transform::<FnP>(upper);
312 let result = transform_fn(sample_person());
313 assert_eq!(result.name, "ALICE");
314 assert_eq!(result.age, 30);
315 }
316
317 #[derive(Debug, Clone, PartialEq)]
320 struct Address {
321 street: String,
322 city: String,
323 }
324
325 #[derive(Debug, Clone, PartialEq)]
326 struct Company {
327 name: String,
328 ceo: Person,
329 }
330
331 fn company_ceo_lens() -> SimpleLens<Company, Person> {
332 Lens::new(|c: &Company| c.ceo.clone(), |c, ceo| Company { ceo, ..c })
333 }
334
335 fn address_city_lens() -> SimpleLens<Address, String> {
336 Lens::new(
337 |a: &Address| a.city.clone(),
338 |a, city| Address { city, ..a },
339 )
340 }
341
342 fn address_street_lens() -> SimpleLens<Address, String> {
343 Lens::new(
344 |a: &Address| a.street.clone(),
345 |a, street| Address { street, ..a },
346 )
347 }
348
349 fn sample_company() -> Company {
350 Company {
351 name: "Acme".to_string(),
352 ceo: sample_person(),
353 }
354 }
355
356 #[test]
358 fn composed_get() {
359 let lens = company_ceo_lens().then(person_name_lens());
360 assert_eq!(lens.get(&sample_company()), "Alice");
361 }
362
363 #[test]
364 fn composed_set() {
365 let lens = company_ceo_lens().then(person_name_lens());
366 let updated = lens.set(sample_company(), "Bob".to_string());
367 assert_eq!(updated.ceo.name, "Bob");
368 assert_eq!(updated.ceo.age, 30);
369 assert_eq!(updated.name, "Acme");
370 }
371
372 #[test]
373 fn composed_over() {
374 let lens = company_ceo_lens().then(person_age_lens());
375 let updated = lens.over(sample_company(), |age| age + 1);
376 assert_eq!(updated.ceo.age, 31);
377 assert_eq!(updated.ceo.name, "Alice");
378 }
379
380 #[derive(Debug, Clone, PartialEq)]
383 struct PersonWithAddr {
384 name: String,
385 addr: Address,
386 }
387
388 #[derive(Debug, Clone, PartialEq)]
389 struct Org {
390 title: String,
391 lead: PersonWithAddr,
392 }
393
394 fn org_lead_lens() -> SimpleLens<Org, PersonWithAddr> {
395 Lens::new(|o: &Org| o.lead.clone(), |o, lead| Org { lead, ..o })
396 }
397
398 fn pwa_addr_lens() -> SimpleLens<PersonWithAddr, Address> {
399 Lens::new(
400 |p: &PersonWithAddr| p.addr.clone(),
401 |p, addr| PersonWithAddr { addr, ..p },
402 )
403 }
404
405 fn sample_org() -> Org {
406 Org {
407 title: "R&D".to_string(),
408 lead: PersonWithAddr {
409 name: "Alice".to_string(),
410 addr: Address {
411 street: "123 Main St".to_string(),
412 city: "Springfield".to_string(),
413 },
414 },
415 }
416 }
417
418 #[test]
419 fn three_deep_get() {
420 let lens = org_lead_lens()
421 .then(pwa_addr_lens())
422 .then(address_city_lens());
423 assert_eq!(lens.get(&sample_org()), "Springfield");
424 }
425
426 #[test]
427 fn three_deep_set() {
428 let lens = org_lead_lens()
429 .then(pwa_addr_lens())
430 .then(address_city_lens());
431 let updated = lens.set(sample_org(), "Shelbyville".to_string());
432 assert_eq!(updated.lead.addr.city, "Shelbyville");
433 assert_eq!(updated.lead.addr.street, "123 Main St");
434 assert_eq!(updated.lead.name, "Alice");
435 }
436
437 #[test]
438 fn three_deep_over() {
439 let lens = org_lead_lens()
440 .then(pwa_addr_lens())
441 .then(address_street_lens());
442 let updated = lens.over(sample_org(), |s| s.to_uppercase());
443 assert_eq!(updated.lead.addr.street, "123 MAIN ST");
444 }
445
446 proptest! {
450 #[test]
452 fn composed_law_get_put(name in "[a-z]{1,8}", co_name in "[a-z]{1,8}", age in 0u32..1000) {
453 let lens = company_ceo_lens().then(person_age_lens());
454 let c = Company {
455 name: co_name,
456 ceo: Person { name, age },
457 };
458 let result = lens.set(c.clone(), lens.get(&c));
459 prop_assert_eq!(result, c);
460 }
461
462 #[test]
464 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) {
465 let lens = company_ceo_lens().then(person_age_lens());
466 let c = Company {
467 name: co_name,
468 ceo: Person { name, age },
469 };
470 let result = lens.set(c, new_age);
471 prop_assert_eq!(lens.get(&result), new_age);
472 }
473
474 #[test]
476 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) {
477 let lens = company_ceo_lens().then(person_age_lens());
478 let c = Company {
479 name: co_name,
480 ceo: Person { name, age },
481 };
482 let left = lens.set(lens.set(c.clone(), b1), b2);
483 let right = lens.set(c, b2);
484 prop_assert_eq!(left, right);
485 }
486 }
487
488 #[test]
490 fn profunctor_composition_equivalence() {
491 let outer = company_ceo_lens();
492 let inner = person_age_lens();
493 let composed = company_ceo_lens().then(person_age_lens());
494
495 let increment: Box<dyn Fn(u32) -> u32> = Box::new(|age| age + 1);
496 let transform_fn = outer.transform::<FnP>(inner.transform::<FnP>(increment));
497
498 let c = sample_company();
499 let via_profunctor = transform_fn(c.clone());
500 let via_composed = composed.over(c, |age| age + 1);
501 assert_eq!(via_profunctor, via_composed);
502 }
503}