fix prisms
This commit is contained in:
parent
529b628260
commit
bf85419c16
6 changed files with 223 additions and 41 deletions
|
@ -1,5 +1,6 @@
|
|||
use crate::{
|
||||
lenses::{Lens, LensOver, LensView},
|
||||
prisms::Prism,
|
||||
traversals::{Traversal, TraversalOver, TraversalTraverse},
|
||||
};
|
||||
|
||||
|
@ -8,6 +9,7 @@ pub struct Combination<A, B>(A, B);
|
|||
|
||||
// additions
|
||||
|
||||
// lens + lens
|
||||
impl<A, B> std::ops::Add<Lens<B>> for Lens<A> {
|
||||
type Output = Lens<Combination<Lens<A>, Lens<B>>>;
|
||||
|
||||
|
@ -15,6 +17,7 @@ impl<A, B> std::ops::Add<Lens<B>> for Lens<A> {
|
|||
Lens(Combination(self, rhs))
|
||||
}
|
||||
}
|
||||
// traversal + traversal
|
||||
impl<A, B> std::ops::Add<Traversal<B>> for Traversal<A> {
|
||||
type Output = Traversal<Combination<Traversal<A>, Traversal<B>>>;
|
||||
|
||||
|
@ -22,6 +25,7 @@ impl<A, B> std::ops::Add<Traversal<B>> for Traversal<A> {
|
|||
Traversal(Combination(self, rhs))
|
||||
}
|
||||
}
|
||||
// traversal + lens
|
||||
impl<A, B> std::ops::Add<Lens<B>> for Traversal<A> {
|
||||
type Output = Traversal<Combination<Traversal<A>, Traversal<Lens<B>>>>;
|
||||
|
||||
|
@ -29,6 +33,7 @@ impl<A, B> std::ops::Add<Lens<B>> for Traversal<A> {
|
|||
Traversal(Combination(self, rhs.to_traversal()))
|
||||
}
|
||||
}
|
||||
// lens + traversal
|
||||
impl<A, B> std::ops::Add<Traversal<B>> for Lens<A> {
|
||||
type Output = Traversal<Combination<Traversal<Lens<A>>, Traversal<B>>>;
|
||||
|
||||
|
@ -36,6 +41,22 @@ impl<A, B> std::ops::Add<Traversal<B>> for Lens<A> {
|
|||
Traversal(Combination(self.to_traversal(), rhs))
|
||||
}
|
||||
}
|
||||
// traversal + prism
|
||||
impl<A, B> std::ops::Add<Prism<B>> for Traversal<A> {
|
||||
type Output = Traversal<Combination<Traversal<A>, Traversal<Prism<B>>>>;
|
||||
|
||||
fn add(self, rhs: Prism<B>) -> Self::Output {
|
||||
Traversal(Combination(self, rhs.to_traversal()))
|
||||
}
|
||||
}
|
||||
// prism + traversal
|
||||
impl<A, B> std::ops::Add<Traversal<B>> for Prism<A> {
|
||||
type Output = Traversal<Combination<Traversal<Prism<A>>, Traversal<B>>>;
|
||||
|
||||
fn add(self, rhs: Traversal<B>) -> Self::Output {
|
||||
Traversal(Combination(self.to_traversal(), rhs))
|
||||
}
|
||||
}
|
||||
|
||||
// trait impls for Combination
|
||||
|
||||
|
|
63
src/fns.rs
63
src/fns.rs
|
@ -1,5 +1,6 @@
|
|||
use crate::{
|
||||
lenses::{Lens, LensOver, LensView},
|
||||
prisms::{Prism, PrismPreview},
|
||||
traversals::{Traversal, TraversalOver, TraversalTraverse},
|
||||
};
|
||||
|
||||
|
@ -120,3 +121,65 @@ where
|
|||
L::over(&self.0, args.0, args.1)
|
||||
}
|
||||
}
|
||||
|
||||
// prism preview
|
||||
impl<L, A> std::ops::FnOnce<(A,)> for Prism<L>
|
||||
where
|
||||
L: PrismPreview<A>,
|
||||
{
|
||||
type Output = Option<L::Field>;
|
||||
|
||||
extern "rust-call" fn call_once(self, args: (A,)) -> Self::Output {
|
||||
L::preview(&self.0, args.0)
|
||||
}
|
||||
}
|
||||
impl<L, A> std::ops::FnMut<(A,)> for Prism<L>
|
||||
where
|
||||
L: PrismPreview<A>,
|
||||
{
|
||||
extern "rust-call" fn call_mut(&mut self, args: (A,)) -> Self::Output {
|
||||
L::preview(&self.0, args.0)
|
||||
}
|
||||
}
|
||||
impl<L, A> std::ops::Fn<(A,)> for Prism<L>
|
||||
where
|
||||
L: PrismPreview<A>,
|
||||
{
|
||||
extern "rust-call" fn call(&self, args: (A,)) -> Self::Output {
|
||||
L::preview(&self.0, args.0)
|
||||
}
|
||||
}
|
||||
|
||||
// prism over
|
||||
impl<L, A, F> std::ops::FnOnce<(A, F)> for Prism<L>
|
||||
where
|
||||
A: Clone,
|
||||
L: PrismPreview<A>,
|
||||
F: FnMut(L::Field) -> L::Field,
|
||||
{
|
||||
type Output = A;
|
||||
|
||||
extern "rust-call" fn call_once(self, args: (A, F)) -> Self::Output {
|
||||
L::over(&self.0, args.0, args.1)
|
||||
}
|
||||
}
|
||||
impl<L, A, F> std::ops::FnMut<(A, F)> for Prism<L>
|
||||
where
|
||||
A: Clone,
|
||||
L: PrismPreview<A>,
|
||||
F: FnMut(L::Field) -> L::Field,
|
||||
{
|
||||
extern "rust-call" fn call_mut(&mut self, args: (A, F)) -> Self::Output {
|
||||
L::over(&self.0, args.0, args.1)
|
||||
}
|
||||
}
|
||||
impl<L, A, F> std::ops::Fn<(A, F)> for Prism<L>
|
||||
where
|
||||
A: Clone,
|
||||
L: PrismPreview<A>,
|
||||
F: FnMut(L::Field) -> L::Field,
|
||||
{
|
||||
extern "rust-call" fn call(&self, args: (A, F)) -> Self::Output {
|
||||
L::over(&self.0, args.0, args.1)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,10 +12,28 @@ pub struct Prism<P>(pub(crate) P);
|
|||
pub trait PrismPreview<T> {
|
||||
type Field;
|
||||
|
||||
fn preview(thing: T) -> Option<Self::Field>;
|
||||
}
|
||||
pub trait PrismReview<T>: PrismPreview<T> {
|
||||
fn review(thing: Self::Field) -> T;
|
||||
fn preview(&self, thing: T) -> Option<Self::Field>;
|
||||
fn review(&self, thing: Self::Field) -> T;
|
||||
// TODO id like for this to not need clone
|
||||
fn over<F>(&self, thing: T, f: F) -> T
|
||||
where
|
||||
F: FnOnce(Self::Field) -> Self::Field,
|
||||
T: Clone,
|
||||
{
|
||||
if let Some(a) = Self::preview(&self, thing.clone()) {
|
||||
Self::review(&self, f(a))
|
||||
} else {
|
||||
thing
|
||||
}
|
||||
}
|
||||
|
||||
fn set(&self, thing: T, v: Self::Field) -> T
|
||||
where
|
||||
T: Clone,
|
||||
Self::Field: Clone,
|
||||
{
|
||||
Self::over(self, thing, move |_| v.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, T> PrismPreview<T> for Prism<P>
|
||||
|
@ -24,25 +42,27 @@ where
|
|||
{
|
||||
type Field = P::Field;
|
||||
|
||||
fn preview(thing: T) -> Option<Self::Field> {
|
||||
P::preview(thing)
|
||||
fn preview(&self, thing: T) -> Option<Self::Field> {
|
||||
P::preview(&self.0, thing)
|
||||
}
|
||||
|
||||
fn review(&self, thing: Self::Field) -> T {
|
||||
P::review(&self.0, thing)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, T> PrismReview<T> for Prism<P>
|
||||
where
|
||||
P: PrismReview<T>,
|
||||
{
|
||||
fn review(thing: Self::Field) -> T {
|
||||
P::review(thing)
|
||||
}
|
||||
pub fn preview<T, P: PrismPreview<T>>(prism: P, thing: T) -> Option<P::Field> {
|
||||
P::preview(&prism, thing)
|
||||
}
|
||||
|
||||
pub fn preview<T, P: PrismPreview<T>>(_prism: P, thing: T) -> Option<P::Field> {
|
||||
P::preview(thing)
|
||||
pub fn review<T, P: PrismPreview<T>>(prism: P, thing: P::Field) -> T {
|
||||
P::review(&prism, thing)
|
||||
}
|
||||
pub fn review<T, P: PrismReview<T>>(_prism: P, thing: P::Field) -> T {
|
||||
P::review(thing)
|
||||
pub fn over<T: Clone, P: PrismPreview<T>>(
|
||||
prism: P,
|
||||
thing: T,
|
||||
f: impl FnOnce(P::Field) -> P::Field,
|
||||
) -> T {
|
||||
P::over(&prism, thing, f)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -52,13 +72,13 @@ mod tests {
|
|||
#[test]
|
||||
fn preview_result() {
|
||||
let a: Result<i32, i32> = Ok(3);
|
||||
assert_eq!(preview(_Ok, a), Some(3));
|
||||
assert_eq!(_Ok(a), Some(3));
|
||||
|
||||
let a: Result<i32, i32> = Err(3);
|
||||
assert_eq!(preview(_Ok, a), None);
|
||||
|
||||
let a: Result<i32, i32> = Ok(3);
|
||||
assert_eq!(preview(_Err, a), None);
|
||||
assert_eq!(_Err(a), None);
|
||||
|
||||
let a: Result<i32, i32> = Err(3);
|
||||
assert_eq!(preview(_Err, a), Some(3));
|
||||
|
@ -67,7 +87,7 @@ mod tests {
|
|||
#[test]
|
||||
fn preview_option() {
|
||||
let a = Some(3);
|
||||
assert_eq!(preview(_Some, a), Some(3));
|
||||
assert_eq!(_Some(a), Some(3));
|
||||
|
||||
let a = Some(3);
|
||||
assert_eq!(preview(_None, a), Some(()));
|
||||
|
@ -90,4 +110,11 @@ mod tests {
|
|||
assert_eq!(review(_Some, 3), Some(3));
|
||||
assert_eq!(review(_None, ()), None::<()>);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn over_option() {
|
||||
assert_eq!(over(_Some, Some(3), |v| v + 1), Some(4));
|
||||
assert_eq!(_Some(Some(3), |v| v + 1), Some(4));
|
||||
assert_eq!(over(_None, None, |_v: ()| ()), None::<()>);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,13 +8,11 @@ pub const _Some: Prism<SomeInner> = Prism(SomeInner);
|
|||
impl<T> PrismPreview<Option<T>> for SomeInner {
|
||||
type Field = T;
|
||||
|
||||
fn preview(thing: Option<T>) -> Option<Self::Field> {
|
||||
fn preview(&self, thing: Option<T>) -> Option<Self::Field> {
|
||||
thing
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PrismReview<Option<T>> for SomeInner {
|
||||
fn review(thing: Self::Field) -> Option<T> {
|
||||
fn review(&self, thing: Self::Field) -> Option<T> {
|
||||
Some(thing)
|
||||
}
|
||||
}
|
||||
|
@ -26,13 +24,11 @@ pub const _None: Prism<NoneInner> = Prism(NoneInner);
|
|||
impl<T> PrismPreview<Option<T>> for NoneInner {
|
||||
type Field = ();
|
||||
|
||||
fn preview(_thing: Option<T>) -> Option<Self::Field> {
|
||||
fn preview(&self, _thing: Option<T>) -> Option<Self::Field> {
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PrismReview<Option<T>> for NoneInner {
|
||||
fn review(_thing: Self::Field) -> Option<T> {
|
||||
fn review(&self, _thing: Self::Field) -> Option<T> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,10 @@ pub const _Ok: Prism<OkInner> = Prism(OkInner);
|
|||
impl<T, E> PrismPreview<Result<T, E>> for OkInner {
|
||||
type Field = T;
|
||||
|
||||
fn preview(thing: Result<T, E>) -> Option<Self::Field> {
|
||||
fn preview(&self, thing: Result<T, E>) -> Option<Self::Field> {
|
||||
thing.ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> PrismReview<Result<T, E>> for OkInner {
|
||||
fn review(thing: Self::Field) -> Result<T, E> {
|
||||
fn review(&self, thing: Self::Field) -> Result<T, E> {
|
||||
Ok(thing)
|
||||
}
|
||||
}
|
||||
|
@ -26,13 +23,10 @@ pub const _Err: Prism<ErrInner> = Prism(ErrInner);
|
|||
impl<T, E> PrismPreview<Result<T, E>> for ErrInner {
|
||||
type Field = E;
|
||||
|
||||
fn preview(thing: Result<T, E>) -> Option<Self::Field> {
|
||||
fn preview(&self, thing: Result<T, E>) -> Option<Self::Field> {
|
||||
thing.err()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> PrismReview<Result<T, E>> for ErrInner {
|
||||
fn review(thing: Self::Field) -> Result<T, E> {
|
||||
fn review(&self, thing: Self::Field) -> Result<T, E> {
|
||||
Err(thing)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,10 @@ pub use both::both;
|
|||
mod each;
|
||||
pub use each::each;
|
||||
|
||||
use crate::lenses::{Lens, LensOver, LensView};
|
||||
use crate::{
|
||||
lenses::{Lens, LensOver, LensView},
|
||||
prisms::{Prism, PrismPreview},
|
||||
};
|
||||
|
||||
/// Wrapper type
|
||||
#[derive(Clone, Copy)]
|
||||
|
@ -87,6 +90,43 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// all prisms are traversals, so we can freely transform them into a traversal
|
||||
impl<L> Prism<L> {
|
||||
/// Returns this lens as a traversal
|
||||
pub fn to_traversal(self) -> Traversal<Prism<L>> {
|
||||
Traversal(self)
|
||||
}
|
||||
}
|
||||
// we can go back to a lens from a "traversal-ed" lens
|
||||
impl<L> Traversal<Prism<L>> {
|
||||
/// Returns the wrapped lens
|
||||
pub fn to_prism(self) -> Prism<L> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
impl<L, T> TraversalTraverse<T> for Prism<L>
|
||||
where
|
||||
L: PrismPreview<T>,
|
||||
{
|
||||
type Field = L::Field;
|
||||
|
||||
fn traverse(&self, thing: T) -> Vec<Self::Field> {
|
||||
L::preview(&self.0, thing).into_iter().collect()
|
||||
}
|
||||
}
|
||||
impl<L, T> TraversalOver<T> for Prism<L>
|
||||
where
|
||||
T: Clone,
|
||||
L: PrismPreview<T>,
|
||||
{
|
||||
fn over<F>(&self, thing: T, f: F) -> T
|
||||
where
|
||||
F: FnMut(Self::Field) -> Self::Field,
|
||||
{
|
||||
L::over(&self.0, thing, f)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn traverse<T, L: TraversalTraverse<T>>(lens: L, thing: T) -> Vec<L::Field> {
|
||||
L::traverse(&lens, thing)
|
||||
}
|
||||
|
@ -102,7 +142,7 @@ pub fn over<T, L: TraversalOver<T>>(lens: L, thing: T, f: impl FnMut(L::Field) -
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::lenses::_0;
|
||||
use crate::{lenses::_0, prisms::_Some};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -157,4 +197,45 @@ mod tests {
|
|||
let res = t(array, |v| v + 1);
|
||||
assert_eq!(res, [(2, 3), (3, 4), (5, 6)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_combine_prism_with_traversal() {
|
||||
let array = [Some(1), None, Some(3), None, Some(5)];
|
||||
|
||||
// combine a traversal with a lens
|
||||
let t = each + _Some;
|
||||
|
||||
// traverse
|
||||
let res = t(array);
|
||||
assert_eq!(res, vec![1, 3, 5]);
|
||||
|
||||
// over
|
||||
let res = t(array, |v| v + 1);
|
||||
assert_eq!(res, [Some(2), None, Some(4), None, Some(6)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_combine_traversal_with_prism() {
|
||||
let array = Some([1, 2, 3]);
|
||||
|
||||
// combine a traversal with a lens
|
||||
let t = _Some + each;
|
||||
|
||||
// traverse
|
||||
let res = t(array);
|
||||
assert_eq!(res, vec![1, 2, 3]);
|
||||
|
||||
// over
|
||||
let res = t(array, |v| v + 1);
|
||||
assert_eq!(res, Some([2, 3, 4]));
|
||||
|
||||
let array: Option<[i32; 3]> = None;
|
||||
// traverse
|
||||
let res = t(array);
|
||||
assert_eq!(res, vec![]);
|
||||
|
||||
// over
|
||||
let res = t(array, |v| v + 1);
|
||||
assert_eq!(res, None);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue