From 6b339ddde1c7fe4b9c467616ce472b137b6a23d1 Mon Sep 17 00:00:00 2001 From: annieversary Date: Thu, 11 Nov 2021 13:50:54 +0000 Subject: [PATCH] combination of traversals --- src/combinations.rs | 59 ++++++++++++++++++++++++++++- src/lenses/lens.rs | 14 +++---- src/lenses/mod.rs | 17 --------- src/lenses/to.rs | 6 +-- src/traversals/mod.rs | 86 ++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 153 insertions(+), 29 deletions(-) diff --git a/src/combinations.rs b/src/combinations.rs index 60eee2b..9fd4a10 100644 --- a/src/combinations.rs +++ b/src/combinations.rs @@ -1,8 +1,13 @@ -use crate::lenses::{Lens, LensOver, LensView}; +use crate::{ + lenses::{Lens, LensOver, LensView}, + traversals::{Traversal, TraversalOver, TraversalTraverse}, +}; #[derive(Clone, Copy)] pub struct Combination(A, B); +// additions + impl std::ops::Add> for Lens { type Output = Lens, Lens>>; @@ -10,6 +15,29 @@ impl std::ops::Add> for Lens { Lens(Combination(self, rhs)) } } +impl std::ops::Add> for Traversal { + type Output = Traversal, Traversal>>; + + fn add(self, rhs: Traversal) -> Self::Output { + Traversal(Combination(self, rhs)) + } +} +impl std::ops::Add> for Traversal { + type Output = Traversal, Traversal>>>; + + fn add(self, rhs: Lens) -> Self::Output { + Traversal(Combination(self, rhs.to_traversal())) + } +} +impl std::ops::Add> for Lens { + type Output = Traversal>, Traversal>>; + + fn add(self, rhs: Traversal) -> Self::Output { + Traversal(Combination(self.to_traversal(), rhs)) + } +} + +// trait impls for Combination impl LensView for Combination where @@ -35,3 +63,32 @@ where A::over(&self.0, thing, |b| B::over(&self.1, b, f)) } } + +impl TraversalTraverse for Combination +where + A: TraversalTraverse, + B: TraversalTraverse, +{ + type Field = B::Field; + + fn traverse(&self, thing: T) -> Vec { + let a = A::traverse(&self.0, thing); + a.into_iter() + .map(|v| B::traverse(&self.1, v)) + .flatten() + .collect() + } +} + +impl TraversalOver for Combination +where + A: TraversalOver, + B: TraversalOver, +{ + fn over(&self, thing: T, mut f: F) -> T + where + F: FnMut(Self::Field) -> Self::Field, + { + A::over(&self.0, thing, |b| B::over(&self.1, b, &mut f)) + } +} diff --git a/src/lenses/lens.rs b/src/lenses/lens.rs index aa83efc..ec8e647 100644 --- a/src/lenses/lens.rs +++ b/src/lenses/lens.rs @@ -3,16 +3,16 @@ use crate::lenses::{Lens, LensOver, LensView}; type Getter = dyn Fn(T) -> U; type Setter = dyn Fn(T, U) -> T; -pub struct LensInner(pub(crate) Box>, pub(crate) Box>); +pub struct FuncLens(pub(crate) Box>, pub(crate) Box>); -impl LensView for LensInner { +impl LensView for FuncLens { type Field = U; fn view(&self, thing: T) -> Self::Field { (self.0)(thing) } } -impl LensOver for LensInner { +impl LensOver for FuncLens { fn over(&self, thing: T, f: F) -> T where F: FnOnce(Self::Field) -> Self::Field, @@ -30,13 +30,13 @@ impl LensOver for LensInner { pub fn lens_from_boxed( getter: Box>, setter: Box>, -) -> Lens> { - Lens(LensInner(getter, setter)) +) -> Lens> { + Lens(FuncLens(getter, setter)) } /// Makes a lens that implements `LensView` and `LensOver` with the provided functions pub fn lens( getter: impl Fn(T) -> U + 'static, setter: impl Fn(T, U) -> T + 'static, -) -> Lens> { - Lens(LensInner(Box::new(getter), Box::new(setter))) +) -> Lens> { + Lens(FuncLens(Box::new(getter), Box::new(setter))) } diff --git a/src/lenses/mod.rs b/src/lenses/mod.rs index e6ce140..9492ab9 100644 --- a/src/lenses/mod.rs +++ b/src/lenses/mod.rs @@ -11,28 +11,11 @@ pub use to::{to, to_from_boxed}; mod lens; pub use lens::{lens, lens_from_boxed}; -use crate::traversals::Traversal; - /// Wrapper type #[derive(Clone, Copy)] #[repr(transparent)] pub struct Lens(pub(crate) L); -// all lenses are traversals, so we can freely transform them into a traversal -impl Lens { - /// Returns this lens as a traversal - pub fn to_traversal(self) -> Traversal> { - Traversal(self) - } -} -// we can go back to a lens from a "traversal-ed" lens -impl Traversal> { - /// Returns the wrapped lens - pub fn to_lens(self) -> Lens { - self.0 - } -} - /// For lenses that allow viewing pub trait LensView { type Field; diff --git a/src/lenses/to.rs b/src/lenses/to.rs index d9309dd..c6e9f24 100644 --- a/src/lenses/to.rs +++ b/src/lenses/to.rs @@ -1,6 +1,6 @@ use crate::lenses::{Lens, LensView}; -use super::lens::LensInner; +use super::lens::FuncLens; pub struct ToInner(Box U>); @@ -23,7 +23,7 @@ pub fn to(f: impl Fn(T) -> U + 'static) -> Lens> { impl Lens> { /// Makes a full lens that implements `LensView` and `LensOver` with the provided functions - pub fn make_lens(self, setter: impl Fn(T, U) -> T + 'static) -> Lens> { - Lens(LensInner((self.0).0, Box::new(setter))) + pub fn make_lens(self, setter: impl Fn(T, U) -> T + 'static) -> Lens> { + Lens(FuncLens((self.0).0, Box::new(setter))) } } diff --git a/src/traversals/mod.rs b/src/traversals/mod.rs index d3cfef8..19043ac 100644 --- a/src/traversals/mod.rs +++ b/src/traversals/mod.rs @@ -4,6 +4,8 @@ pub use both::both; mod each; pub use each::each; +use crate::lenses::{Lens, LensOver, LensView}; + /// Wrapper type #[derive(Clone, Copy)] #[repr(transparent)] @@ -14,7 +16,6 @@ pub trait TraversalTraverse { fn traverse(&self, thing: T) -> Vec; } - pub trait TraversalOver: TraversalTraverse { fn over(&self, thing: T, f: F) -> T where @@ -27,6 +28,7 @@ pub trait TraversalOver: TraversalTraverse { Self::over(self, thing, move |_| v.clone()) } } + impl TraversalTraverse for Traversal where L: TraversalTraverse, @@ -49,6 +51,42 @@ where } } +// all lenses are traversals, so we can freely transform them into a traversal +impl Lens { + /// Returns this lens as a traversal + pub fn to_traversal(self) -> Traversal> { + Traversal(self) + } +} +// we can go back to a lens from a "traversal-ed" lens +impl Traversal> { + /// Returns the wrapped lens + pub fn to_lens(self) -> Lens { + self.0 + } +} +impl TraversalTraverse for Lens +where + L: LensView, +{ + type Field = L::Field; + + fn traverse(&self, thing: T) -> Vec { + vec![L::view(&self.0, thing)] + } +} +impl TraversalOver for Lens +where + L: LensView + LensOver, +{ + fn over(&self, thing: T, f: F) -> T + where + F: FnMut(Self::Field) -> Self::Field, + { + L::over(&self.0, thing, f) + } +} + pub fn traverse>(lens: L, thing: T) -> Vec { L::traverse(&lens, thing) } @@ -64,13 +102,59 @@ pub fn over>(lens: L, thing: T, f: impl FnMut(L::Field) - #[cfg(test)] mod tests { + use crate::lenses::_0; + use super::*; #[test] fn traverse_each_works_on_arrays() { let array = [1, 2, 3, 4]; + let res = each(array); + assert_eq!(res, vec![1, 2, 3, 4,]); + let res = each(array, |v| v + 1); assert_eq!(res, [2, 3, 4, 5]); } + + #[test] + fn can_combine_traversals() { + let array = [vec![1, 2], vec![3, 4]]; + + // combine two traversals + let res = (each + each)(array, |v| v + 1); + assert_eq!(res, [vec![2, 3], vec![4, 5]]); + } + + #[test] + fn can_combine_traversal_with_lens() { + let array = [(1, 2), (3, 4), (5, 6)]; + + // combine a traversal with a lens + let t = each + _0; + + // traverse + let res = t(array); + assert_eq!(res, vec![1, 3, 5]); + + // over + let res = t(array, |v| v + 1); + assert_eq!(res, [(2, 2), (4, 4), (6, 6)]); + } + + #[test] + fn can_combine_lens_with_traversal() { + let array = [(1, 2), (3, 4), (5, 6)]; + + // combine a traversal with a lens + let t = _0 + each; + + // traverse + let res = t(array); + assert_eq!(res, vec![1, 2]); + + // over + let res = t(array, |v| v + 1); + assert_eq!(res, [(2, 3), (3, 4), (5, 6)]); + } }