diff --git a/README.md b/README.md new file mode 100644 index 0000000..dcab469 --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +# bad optics + +ergonomic no-macro lenses for rust + +## example + +```rust +use bad_optics::{ +lenses::{_0, _1}, +*, +}; + +fn main() { + let a = ((1, 2), 3); + + // combine lenses + let lens = _0 + _1; + + // use the view function to access + let res = view(lens, a); + assert_eq!(res, 2); + + // call the lens as a function + let res = lens(a); + assert_eq!(res, 2); + + // call the over function to modify the value + let a = over(lens, a, |v| v + 1); + assert_eq!(a, ((1, 3), 3)); + + // call the set function to set the value + let a = set(lens, a, 5); + assert_eq!(a, ((1, 5), 3)); + + // call the lens as a function to modify the value + let res = lens(a, |v| v + 1); + assert_eq!(res, ((1, 3), 3)); +} +``` diff --git a/examples/main.rs b/examples/main.rs new file mode 100644 index 0000000..1326e48 --- /dev/null +++ b/examples/main.rs @@ -0,0 +1,31 @@ +use bad_optics::{ + lenses::{_0, _1}, + *, +}; + +fn main() { + let a = ((1, 2), 3); + + // combine lenses + let lens = _0 + _1; + + // use the view function to access + let res = view(lens, a); + assert_eq!(res, 2); + + // call the lens as a function + let res = lens(a); + assert_eq!(res, 2); + + // call the over function to modify the value + let a = over(lens, a, |v| v + 1); + assert_eq!(a, ((1, 3), 3)); + + // call the set function to set the value + let a = set(lens, a, 5); + assert_eq!(a, ((1, 5), 3)); + + // call the lens as a function to modify the value + let res = lens(a, |v| v + 1); + assert_eq!(res, ((1, 3), 3)); +} diff --git a/src/combinations.rs b/src/combinations.rs index a874963..be8f024 100644 --- a/src/combinations.rs +++ b/src/combinations.rs @@ -1,5 +1,6 @@ use crate::{Lens, LensOver, LensTrait, LensView}; +#[derive(Clone, Copy)] pub struct Combination(A, B); impl LensTrait for Combination {} diff --git a/src/fns.rs b/src/fns.rs new file mode 100644 index 0000000..95258e6 --- /dev/null +++ b/src/fns.rs @@ -0,0 +1,58 @@ +use crate::{Lens, LensOver, LensView}; + +impl std::ops::FnOnce<(A,)> for Lens +where + L: LensView, +{ + type Output = L::Field; + + extern "rust-call" fn call_once(self, args: (A,)) -> Self::Output { + L::view(args.0) + } +} +impl std::ops::FnMut<(A,)> for Lens +where + L: LensView, +{ + extern "rust-call" fn call_mut(&mut self, args: (A,)) -> Self::Output { + L::view(args.0) + } +} +impl std::ops::Fn<(A,)> for Lens +where + L: LensView, +{ + extern "rust-call" fn call(&self, args: (A,)) -> Self::Output { + L::view(args.0) + } +} + +impl std::ops::FnOnce<(A, F)> for Lens +where + L: LensOver, + F: FnOnce(L::Field) -> L::Field, +{ + type Output = A; + + extern "rust-call" fn call_once(self, args: (A, F)) -> Self::Output { + L::over(args.0, args.1) + } +} +impl std::ops::FnMut<(A, F)> for Lens +where + L: LensOver, + F: FnOnce(L::Field) -> L::Field, +{ + extern "rust-call" fn call_mut(&mut self, args: (A, F)) -> Self::Output { + L::over(args.0, args.1) + } +} +impl std::ops::Fn<(A, F)> for Lens +where + L: LensOver, + F: FnOnce(L::Field) -> L::Field, +{ + extern "rust-call" fn call(&self, args: (A, F)) -> Self::Output { + L::over(args.0, args.1) + } +} diff --git a/src/lenses/first.rs b/src/lenses/first.rs index d8bd328..4615c8a 100644 --- a/src/lenses/first.rs +++ b/src/lenses/first.rs @@ -1,5 +1,6 @@ use crate::{Lens, LensOver, LensTrait, LensView}; +#[derive(Clone, Copy)] pub struct _0Inner; pub const _0: Lens<_0Inner> = Lens(_0Inner); impl LensTrait for _0Inner {} diff --git a/src/lenses/identity.rs b/src/lenses/identity.rs index e62f87c..f1bf46d 100644 --- a/src/lenses/identity.rs +++ b/src/lenses/identity.rs @@ -1,6 +1,7 @@ use crate::{LensOver, LensTrait, LensView}; #[allow(non_camel_case_types)] +#[derive(Clone, Copy)] pub struct id; impl LensTrait for id {} diff --git a/src/lenses/second.rs b/src/lenses/second.rs index 506e78c..d781d9f 100644 --- a/src/lenses/second.rs +++ b/src/lenses/second.rs @@ -1,5 +1,6 @@ use crate::{Lens, LensOver, LensTrait, LensView}; +#[derive(Clone, Copy)] pub struct _1Inner; pub const _1: Lens<_1Inner> = Lens(_1Inner); impl LensTrait for _1Inner {} diff --git a/src/lib.rs b/src/lib.rs index f49eae2..c200846 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(unboxed_closures, fn_traits)] + /// Base trait pub trait LensTrait {} @@ -20,6 +22,7 @@ pub trait LensOver: LensView { } /// Wrapper type +#[derive(Clone, Copy)] pub struct Lens(T); impl LensTrait for Lens {} @@ -55,8 +58,6 @@ pub fn over>(_lens: L, thing: T, f: impl FnOnce(L::Field) -> L L::over(thing, f) } -mod combinations; - // TODO add fn impls // TODO add third_from_tuple, etc @@ -65,6 +66,8 @@ mod combinations; // TODO make over work with changing types +mod combinations; +mod fns; pub mod lenses; #[cfg(test)] @@ -150,4 +153,21 @@ mod tests { let a = over(lens, a, |v| v + 1); assert_eq!(a, ((1, 3), 3)); } + + #[test] + fn call_as_funcs() { + let a = (1, 2); + assert_eq!(_0(a), 1); + + let a = (1, 2); + assert_eq!(_0(a, |v| v + 1), (2, 2)); + + let a = ((1, 2), 3); + let lens = _0 + _1; + + let res = lens(a); + assert_eq!(res, 2); + let res = lens(a, |v| v + 1); + assert_eq!(res, ((1, 3), 3)); + } }