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));
+ }
}