fix prisms

This commit is contained in:
annieversary 2021-11-11 14:47:55 +00:00
parent 529b628260
commit bf85419c16
6 changed files with 223 additions and 41 deletions

View file

@ -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

View file

@ -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)
}
}

View file

@ -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::<()>);
}
}

View file

@ -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
}
}

View file

@ -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)
}
}

View file

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