From 0210e04146b5d63edd43c91592a45f928bc47704 Mon Sep 17 00:00:00 2001 From: Alexander Ronald Altman Date: Sat, 17 Dec 2016 11:35:07 -0800 Subject: [PATCH] Finally works! YAY!!!!! --- src/conv.rs | 32 ++++++++++++++++++++++- src/lib.rs | 19 ++++++++++++++ src/partial_lens.rs | 64 ++++++++++++++++++++++++++++++++++++++++----- src/terminal.rs | 33 ++++++++++++++++++----- 4 files changed, 135 insertions(+), 13 deletions(-) diff --git a/src/conv.rs b/src/conv.rs index c4d5a4a..1887989 100644 --- a/src/conv.rs +++ b/src/conv.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Formatter}; use std::marker::PhantomData; -use super::{Iso, Lens, Lenticuloid, PartialLens, Prism}; +use super::{Injector, Iso, Lens, Lenticuloid, PartialLens, Prism, util}; /// An isomorphism family that handles lossless conversions by owned value. pub struct Conv { @@ -121,6 +121,16 @@ impl PartialLens for Conv Ok(v.into()) } + #[inline] + fn try_get_inject(&self, + v: Self::InitialSource) + -> Result<(Self::InitialTarget, + Injector), + Self::FinalSource> { + Ok((v.into(), + util::once_to_mut(move |x: Self::FinalTarget| -> Self::FinalSource { x.into() }))) + } + #[inline] fn set(&self, _v: Self::InitialSource, x: Self::FinalTarget) -> Self::FinalSource { x.into() @@ -298,6 +308,16 @@ impl<'a, S: ?Sized, A: ?Sized, T: ?Sized, B: ?Sized> PartialLens for ConvRef<'a, Ok(v.as_ref()) } + #[inline] + fn try_get_inject(&self, + v: Self::InitialSource) + -> Result<(Self::InitialTarget, + Injector), + Self::FinalSource> { + Ok((v.as_ref(), + util::once_to_mut(move |x: Self::FinalTarget| -> Self::FinalSource { x.as_ref() }))) + } + #[inline] fn set(&self, _v: Self::InitialSource, x: Self::FinalTarget) -> Self::FinalSource { x.as_ref() @@ -475,6 +495,16 @@ impl<'a, S: ?Sized, A: ?Sized, T: ?Sized, B: ?Sized> PartialLens for ConvMut<'a, Ok(v.as_mut()) } + #[inline] + fn try_get_inject(&self, + v: Self::InitialSource) + -> Result<(Self::InitialTarget, + Injector), + Self::FinalSource> { + Ok((v.as_mut(), + util::once_to_mut(move |x: Self::FinalTarget| -> Self::FinalSource { x.as_mut() }))) + } + #[inline] fn set(&self, _v: Self::InitialSource, x: Self::FinalTarget) -> Self::FinalSource { x.as_mut() diff --git a/src/lib.rs b/src/lib.rs index 1487e7d..497c259 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,10 +10,29 @@ //! - `const fn` support #![cfg_attr(feature = "nightly", feature(never_type, const_fn))] +#![cfg_attr(feature = "cargo-clippy", allow(expl_impl_clone_on_copy, type_complexity))] use std::fmt; use std::marker::PhantomData; +/// Some utility functions used inside this crate, but possibly useful for +/// others as well. +pub mod util { + pub fn once_to_mut<'a, X, Y, F>(f_once: F) -> Box Option + 'a> + where F: FnOnce(X) -> Y + 'a + { + let mut f_opt: Option = Some(f_once); + Box::new(move |x| f_opt.take().map(move |f| f(x))) + } + + pub fn once_to_mut_flatten<'a, X, Y, F>(f_once: F) -> Box Option + 'a> + where F: FnOnce(X) -> Option + 'a + { + let mut f_opt: Option = Some(f_once); + Box::new(move |x| f_opt.take().and_then(move |f| f(x))) + } +} + /// The supertype of all lenticuloids. pub trait Lenticuloid { type InitialSource; diff --git a/src/partial_lens.rs b/src/partial_lens.rs index 7cdf667..746584b 100644 --- a/src/partial_lens.rs +++ b/src/partial_lens.rs @@ -1,11 +1,25 @@ -use super::{Compose, Identity, Invert, Iso, Lens, Lenticuloid, Prism}; +use super::{Compose, Identity, Invert, Iso, Lenticuloid, util}; + +pub type Injector<'l, X, Y> = Box Option + 'l>; /// The supertype of all partial lens families. pub trait PartialLens: Lenticuloid where Self::AtInitial: PartialLens, Self::AtFinal: PartialLens { - fn try_get(&self, v: Self::InitialSource) -> Result; + fn try_get(&self, v: Self::InitialSource) -> Result { + self.try_get_inject(v).map(|(x, _)| x) + } + + /// This signature is somewhat hacky; it awaits resolution of the `FnBox` + /// issue for better design. Notably, the injection function returned by + /// this method will (if law-abiding) only return `Some` exactly once; + /// every time afterwards, it will return `None`. + fn try_get_inject(&self, + v: Self::InitialSource) + -> Result<(Self::InitialTarget, + Injector), + Self::FinalSource>; fn set(&self, v: Self::InitialSource, x: Self::FinalTarget) -> Self::FinalSource { self.modify(v, |_| x) @@ -35,6 +49,11 @@ impl PartialLens for Identity { Ok(v) } + #[inline] + fn try_get_inject(&self, v: S) -> Result<(S, Injector), T> { + Ok((v, util::once_to_mut(|x| x))) + } + #[inline] fn set(&self, _v: S, x: T) -> T { x @@ -66,7 +85,31 @@ impl PartialLens for Compose { fn try_get(&self, v: Self::InitialSource) -> Result { let Compose { first: ref lf, second: ref ls } = *self; - () + ls.try_get_inject(v).and_then(move |(q, mut inj)| { + lf.try_get(q).map_err(move |x| inj(x).unwrap_or_else(|| unreachable!())) + }) + } + + fn try_get_inject(&self, + v: Self::InitialSource) + -> Result<(Self::InitialTarget, + Injector), + Self::FinalSource> { + let Compose { first: ref lf, second: ref ls } = *self; + ls.try_get_inject(v).and_then(move |(q, mut inj_q)| { + let res = lf.try_get_inject(q).map(|(x, mut inj_x)| { + (x, move |y| inj_x(y).unwrap_or_else(|| unreachable!())) + }); + match res { + Ok((x, mut inj_x)) => { + Ok((x, + util::once_to_mut(move |y| { + inj_q(inj_x(y)).unwrap_or_else(|| unreachable!()) + }))) + } + Err(q) => Err(inj_q(q).unwrap_or_else(|| unreachable!())), + } + }) } fn set(&self, v: Self::InitialSource, x: Self::FinalTarget) -> Self::FinalSource { @@ -107,6 +150,15 @@ impl PartialLens for Invert Ok(self.deinvert.inject(v)) } + #[inline] + fn try_get_inject(&self, + v: Self::InitialSource) + -> Result<(Self::InitialTarget, + Injector), + Self::FinalSource> { + Ok((self.deinvert.inject(v), util::once_to_mut(move |x| self.deinvert.get(x)))) + } + #[inline] fn set(&self, _v: Self::InitialSource, x: Self::FinalTarget) -> Self::FinalSource { self.deinvert.get(x) @@ -117,7 +169,7 @@ impl PartialLens for Invert v: Self::InitialSource, x: Self::FinalTarget) -> (Option, Self::FinalSource) { - let ref l = self.deinvert; + let l = &self.deinvert; (Some(l.inject(v)), l.get(x)) } @@ -125,7 +177,7 @@ impl PartialLens for Invert fn modify(&self, v: Self::InitialSource, f: F) -> Self::FinalSource where F: FnOnce(Self::InitialTarget) -> Self::FinalTarget { - let ref l = self.deinvert; + let l = &self.deinvert; l.get(f(l.inject(v))) } @@ -133,7 +185,7 @@ impl PartialLens for Invert fn modify_with(&self, v: Self::InitialSource, f: F) -> (Self::FinalSource, Option) where F: FnOnce(Self::InitialTarget) -> (Self::FinalTarget, X) { - let ref l = self.deinvert; + let l = &self.deinvert; let (x, ret) = f(l.inject(v)); (l.get(x), Some(ret)) } diff --git a/src/terminal.rs b/src/terminal.rs index 14af06e..132e156 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -4,7 +4,7 @@ use std::fmt::{self, Debug, Formatter}; use std::marker::PhantomData; -use super::{Lens, Lenticuloid, PartialLens, Prism}; +use super::{Injector, Lens, Lenticuloid, PartialLens, Prism, util}; #[cfg(feature = "nightly")] /// A `Lens` to "extract" anything from `!`. @@ -76,11 +76,16 @@ impl Lenticuloid for FromNever { } #[cfg(feature = "nightly")] +#[cfg_attr(feature = "cargo-clippy", allow(unreachable_code))] impl PartialLens for FromNever { fn try_get(&self, v: !) -> Result { v } + fn try_get_inject(&self, v: !) -> Result<(A, Injector), !> { + v + } + fn set(&self, v: !, _x: B) -> ! { v } @@ -93,7 +98,7 @@ impl PartialLens for FromNever { v } - fn modify_with(&self, v: !, f: F) -> (!, Option) + fn modify_with(&self, v: !, _f: F) -> (!, Option) where F: FnOnce(A) -> (B, X) { v @@ -101,6 +106,7 @@ impl PartialLens for FromNever { } #[cfg(feature = "nightly")] +#[cfg_attr(feature = "cargo-clippy", allow(unreachable_code))] impl Lens for FromNever { fn get(&self, v: !) -> A { v @@ -177,11 +183,16 @@ impl Lenticuloid for ToNever { } #[cfg(feature = "nightly")] +#[cfg_attr(feature = "cargo-clippy", allow(unreachable_code))] impl PartialLens for ToNever { fn try_get(&self, v: S) -> Result { Err(v) } + fn try_get_inject(&self, v: S) -> Result<(!, Injector), S> { + Err(v) + } + fn set(&self, _v: S, x: !) -> S { x } @@ -196,7 +207,7 @@ impl PartialLens for ToNever { v } - fn modify_with(&self, v: S, f: F) -> (S, Option) + fn modify_with(&self, v: S, _f: F) -> (S, Option) where F: FnOnce(!) -> (!, X) { (v, None) @@ -204,6 +215,7 @@ impl PartialLens for ToNever { } #[cfg(feature = "nightly")] +#[cfg_attr(feature = "cargo-clippy", allow(unreachable_code))] impl Prism for ToNever { fn inject(&self, v: !) -> S { v @@ -284,11 +296,15 @@ impl PartialLens for FromUnit { Err(v) } + fn try_get_inject(&self, v: ()) -> Result<(A, Injector), ()> { + Err(v) + } + fn set(&self, v: (), _x: B) -> () { v } - fn exchange(&self, v: (), x: B) -> (Option, ()) { + fn exchange(&self, _v: (), _x: B) -> (Option, ()) { (None, ()) } @@ -298,7 +314,7 @@ impl PartialLens for FromUnit { v } - fn modify_with(&self, v: (), f: F) -> ((), Option) + fn modify_with(&self, _v: (), _f: F) -> ((), Option) where F: FnOnce(A) -> (B, X) { ((), None) @@ -385,6 +401,10 @@ impl PartialLens for ToUnit { Ok(()) } + fn try_get_inject(&self, v: S) -> Result<((), Injector<(), S>), S> { + Ok(((), util::once_to_mut(|_| v))) + } + fn set(&self, v: S, _x: ()) -> S { v } @@ -393,7 +413,8 @@ impl PartialLens for ToUnit { (Some(()), v) } - fn modify ()>(&self, v: S, _f: F) -> S { + fn modify ()>(&self, v: S, f: F) -> S { + f(()); v }