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