add to, lens

main
annieversary 2021-11-11 11:07:40 +00:00
parent 61484339f9
commit 2c0a770f9f
3 changed files with 128 additions and 0 deletions

46
src/lenses/lens.rs Normal file
View File

@ -0,0 +1,46 @@
use crate::{
lenses::{LensOver, LensView},
Optics, OpticsTrait,
};
type Getter<T, U> = dyn Fn(T) -> U;
type Setter<T, U> = dyn Fn(T, U) -> T;
pub struct LensInner<T, U>(pub(crate) Box<Getter<T, U>>, pub(crate) Box<Setter<T, U>>);
impl<T, U> OpticsTrait for LensInner<T, U> {}
impl<T, U> LensView<T> for LensInner<T, U> {
type Field = U;
fn view(&self, thing: T) -> Self::Field {
(self.0)(thing)
}
}
impl<T: Clone, U> LensOver<T> for LensInner<T, U> {
fn over<F>(&self, thing: T, f: F) -> T
where
F: FnOnce(Self::Field) -> Self::Field,
{
let v = f((self.0)(thing.clone()));
(self.1)(thing, v)
}
fn set(&self, thing: T, v: Self::Field) -> T {
(self.1)(thing, v)
}
}
/// Makes a lens that implements `LensView<T>` and `LensOver<T>` with the provided functions
pub fn lens_from_boxed<T, U>(
getter: Box<Getter<T, U>>,
setter: Box<Setter<T, U>>,
) -> Optics<LensInner<T, U>> {
Optics(LensInner(getter, setter))
}
/// Makes a lens that implements `LensView<T>` and `LensOver<T>` with the provided functions
pub fn lens<T, U>(
getter: impl Fn(T) -> U + 'static,
setter: impl Fn(T, U) -> T + 'static,
) -> Optics<LensInner<T, U>> {
Optics(LensInner(Box::new(getter), Box::new(setter)))
}

View File

@ -7,6 +7,9 @@ mod second;
pub use second::_1; pub use second::_1;
mod to; mod to;
pub use to::{to, to_from_boxed};
mod lens;
pub use lens::{lens, lens_from_boxed};
use crate::{Optics, OpticsTrait}; use crate::{Optics, OpticsTrait};
@ -161,4 +164,53 @@ mod tests {
let res = lens(a, |v| v + 1); let res = lens(a, |v| v + 1);
assert_eq!(res, ((1, 3), 3)); assert_eq!(res, ((1, 3), 3));
} }
#[derive(Debug, PartialEq, Clone)]
struct Hello {
hey: u8,
}
#[test]
fn can_use_to() {
// making a getter
let l = to(|hello: Hello| hello.hey);
let hello = Hello { hey: 8 };
assert_eq!(l(hello), 8);
}
#[test]
fn can_make_lens_out_of_funcs() {
// making a lens
let l = lens(
|hello: Hello| hello.hey,
|mut hello: Hello, v: u8| {
hello.hey = v;
hello
},
);
let hello = Hello { hey: 8 };
assert_eq!(l(hello), 8);
let hello = Hello { hey: 8 };
assert_eq!(l(hello, |v| v + 1), Hello { hey: 9 });
}
#[test]
fn can_make_lens_out_of_to() {
// we first use to, and then use that to make a full lens
let l = to(|hello: Hello| hello.hey);
let l = l.make_lens(|mut hello: Hello, v: u8| {
hello.hey = v;
hello
});
let hello = Hello { hey: 8 };
assert_eq!(l(hello), 8);
let hello = Hello { hey: 8 };
assert_eq!(l(hello, |v| v + 1), Hello { hey: 9 });
}
} }

30
src/lenses/to.rs Normal file
View File

@ -0,0 +1,30 @@
use crate::{lenses::LensView, Optics, OpticsTrait};
use super::lens::LensInner;
pub struct ToInner<T, U>(Box<dyn Fn(T) -> U>);
impl<T, U> OpticsTrait for ToInner<T, U> {}
impl<T, U> LensView<T> for ToInner<T, U> {
type Field = U;
fn view(&self, thing: T) -> Self::Field {
(self.0)(thing)
}
}
/// Makes a lens that implements `LensView<T>` with the provided function
pub fn to_from_boxed<T, U>(f: Box<dyn Fn(T) -> U>) -> Optics<ToInner<T, U>> {
Optics(ToInner(f))
}
/// Makes a lens that implements `LensView<T>` with the provided function
pub fn to<T, U>(f: impl Fn(T) -> U + 'static) -> Optics<ToInner<T, U>> {
Optics(ToInner(Box::new(f)))
}
impl<T, U> Optics<ToInner<T, U>> {
/// Makes a full lens that implements `LensView<T>` and `LensOver<T>` with the provided functions
pub fn make_lens(self, setter: impl Fn(T, U) -> T + 'static) -> Optics<LensInner<T, U>> {
Optics(LensInner((self.0).0, Box::new(setter)))
}
}