1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
/*
* optics: yet another Haskell optics in Rust.
* Copyright (c) 2022 Ruifeng Xie
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//! Traits for polymorphic lens hierarchy.
//!
#![doc = include_str!("../optics.svg")]
use std::convert::Infallible;
use std::fmt::{Debug, Display};
use crate::concrete::{MapError, MapFallible, MapFallibleTo, MapSuccess};
/// Any optics: a view type associated.
pub trait Optics<T: ?Sized> {
/// View type for this optics.
type View: ?Sized;
}
/// Optics with a single possible source type.
pub trait OpticsKnownSource: Optics<Self::Source> {
/// Source type for this optics.
type Source: ?Sized;
}
/// Optics with a success type and an error type associated.
pub trait OpticsFallible {
/// Success type for this optics.
type Success;
/// Error type for this optics.
type Error;
/// Get a lightweight witness for success.
fn success_witness(&self) -> Self::Success;
/// Map the `Success` and `Error` type for this fallible optics.
fn map_fallible<S, F, E, G>(self, f: F, g: G) -> MapFallible<Self, F, G>
where Self: Sized, F: Fn(Self::Success) -> S, G: Fn(Self::Error) -> E {
MapFallible(self, f, g)
}
/// Map the `Success` type for this fallible optics.
fn map_success<S, F>(self, f: F) -> MapSuccess<Self, F>
where Self: Sized, F: Fn(Self::Success) -> S {
MapFallible(self, f, std::convert::identity)
}
/// Map the `Error` type for this fallible optics.
fn map_error<E, G>(self, g: G) -> MapError<Self, G>
where Self: Sized, G: Fn(Self::Error) -> E {
MapFallible(self, std::convert::identity, g)
}
/// Assert that this optics should never fail (in practice).
/// The resulting optics panics on error.
fn assert_infallible(self) -> MapFallibleTo<Self, Self::Success, Infallible>
where Self: Sized, Self::Error: Debug {
self.map_error(|err| panic!("unexpected failure: {err:?}"))
}
/// Map the `Error` type to `Box<str>`.
fn to_str_err(self) -> MapFallibleTo<Self, Self::Success, String>
where Self: Sized, Self::Error: Display {
self.map_error(|err| err.to_string())
}
}
/// AffineFold: getter, but may fail.
pub trait AffineFold<T>: Optics<T> + OpticsFallible where Self::View: Sized {
/// Retrieve the value targeted by an AffineFold.
fn preview(&self, s: T) -> Result<Self::View, Self::Error>;
}
/// AffineFold, with shared references.
pub trait AffineFoldRef<'a, T: ?Sized>: Optics<T> + OpticsFallible where Self::View: 'a {
/// Retrieve a shared reference the value targeted by an AffineFold.
fn preview_ref(&self, s: &'a T) -> Result<&'a Self::View, Self::Error>;
}
/// AffineFold, with mutable references.
pub trait AffineFoldMut<'a, T: ?Sized>: Optics<T> + OpticsFallible where Self::View: 'a {
/// Retrieve a mutable reference the value targeted by an AffineFold.
fn preview_mut(&self, s: &'a mut T) -> Result<&'a mut Self::View, Self::Error>;
}
/// Getter.
pub trait Getter<T>: AffineFold<T> where Self::View: Sized {
/// View the value pointed to by a getter.
fn view(&self, s: T) -> Self::View;
}
/// Getter, with shared references.
pub trait GetterRef<'a, T: ?Sized>: AffineFoldRef<'a, T> where Self::View: 'a {
/// Get a shared reference to the value pointed to by a getter.
fn view_ref(&self, s: &'a T) -> &'a Self::View;
}
/// Getter, with mutable references.
pub trait GetterMut<'a, T: ?Sized>: AffineFoldMut<'a, T> where Self::View: 'a {
/// Get a mutable reference to the value pointed to by a getter.
fn view_mut(&self, s: &'a mut T) -> &'a mut Self::View;
}
/// Review: dual of getter.
pub trait Review<T>: Optics<T> where Self::View: Sized {
/// Retrieve the value targeted by a review.
fn review(&self, a: Self::View) -> T;
}
/// Isomorphisms: getter and review.
pub trait Iso<T>: Getter<T> + Review<T> + Lens<T> + Prism<T> where Self::View: Sized {}
/// Setter.
pub trait Setter<T>: Optics<T> where Self::View: Sized {
/// Apply a setter as a modifier.
fn over(&self, s: &mut T, f: &mut dyn FnMut(&mut Self::View));
/// Apply a setter.
///
/// # Note
/// The value to be set is cloned, because we don't know the exact number of holes to be filled
/// in. If the optics has a stricter interface (i.e., it also implements [`AffineTraversal`]),
/// use [`AffineTraversal::set`] instead.
fn set_cloned(&self, a: &Self::View, s: &mut T) where Self::View: Clone {
self.over(s, &mut |p| *p = a.clone())
}
}
/// Traversal (and also Fold).
pub trait Traversal<T>: Setter<T> where Self::View: Sized {
/// Evaluate the action from left to right on each element targeted by a Traversal.
fn traverse(&self, s: T, f: &mut dyn FnMut(Self::View));
/// Fold every element targeted by this Traversal into a single result.
fn fold<C>(&self, s: T, mut init: C, mut f: impl FnMut(&mut C, Self::View)) -> C {
self.traverse(s, &mut |x| f(&mut init, x));
init
}
/// Flatten the elements targeted by this Traversal into a [`Vec`].
fn flatten(&self, s: T) -> Vec<Self::View> {
self.fold(s, Vec::new(), |res, x| res.push(x))
}
}
/// AffineTraversal: usually composition of [`Lens`]es and [`Prism`]s.
pub trait AffineTraversal<T>: Traversal<T> + AffineFold<T> where Self::View: Sized {
/// Restricted version for [`Setter::over`]. Custom implementation recommended.
fn map(&self, s: &mut T, f: impl FnOnce(&mut Self::View)) {
let mut f = Some(f);
self.over(s, &mut move |p| std::mem::take(&mut f)
.expect("this optics should be affine")(p))
}
/// Apply a setter. No [`Clone`] is needed, because this optics is _affine_.
fn set(&self, s: &mut T, a: Self::View) {
self.map(s, |p| *p = a)
}
}
/// Lens: getter and setter.
pub trait Lens<T>: Getter<T> + AffineTraversal<T> where Self::View: Sized {}
/// Prism: review and setter.
pub trait Prism<T>: Review<T> + AffineTraversal<T> where Self::View: Sized {}