use super::functions::*;
#[allow(dead_code)]
pub struct EitherTMonad<M, E, A> {
pub inner: Vec<OxiEither<A, E>>,
_phantom: std::marker::PhantomData<M>,
}
#[allow(dead_code)]
impl<M, E: Clone, A: Clone> EitherTMonad<M, E, A> {
pub fn new(value: OxiEither<A, E>) -> Self {
Self {
inner: vec![value],
_phantom: std::marker::PhantomData,
}
}
pub fn pure(a: A) -> Self {
Self::new(OxiEither::Left(a))
}
pub fn throw(e: E) -> Self {
Self::new(OxiEither::Right(e))
}
pub fn run(self) -> Option<OxiEither<A, E>> {
self.inner.into_iter().next()
}
pub fn bind<B: Clone, F>(self, f: F) -> EitherTMonad<M, E, B>
where
F: FnOnce(A) -> EitherTMonad<M, E, B>,
{
match self.run() {
Some(OxiEither::Left(a)) => f(a),
Some(OxiEither::Right(e)) => EitherTMonad::<M, E, B>::throw(e),
None => EitherTMonad {
inner: vec![],
_phantom: std::marker::PhantomData,
},
}
}
pub fn map<B: Clone, F: FnOnce(A) -> B>(self, f: F) -> EitherTMonad<M, E, B> {
match self.run() {
Some(OxiEither::Left(a)) => EitherTMonad::<M, E, B>::new(OxiEither::Left(f(a))),
Some(OxiEither::Right(e)) => EitherTMonad::<M, E, B>::new(OxiEither::Right(e)),
None => EitherTMonad {
inner: vec![],
_phantom: std::marker::PhantomData,
},
}
}
}
#[allow(dead_code)]
pub struct EitherKleisli<E, A, B> {
pub run: Box<dyn Fn(A) -> OxiEither<B, E>>,
}
#[allow(dead_code)]
impl<E: 'static, A: 'static, B: 'static> EitherKleisli<E, A, B> {
pub fn new<F: Fn(A) -> OxiEither<B, E> + 'static>(f: F) -> Self {
Self { run: Box::new(f) }
}
pub fn apply(&self, a: A) -> OxiEither<B, E> {
(self.run)(a)
}
pub fn compose<C: 'static>(self, g: EitherKleisli<E, B, C>) -> EitherKleisli<E, A, C>
where
E: Clone + 'static,
{
EitherKleisli::new(move |a: A| match (self.run)(a) {
OxiEither::Left(b) => (g.run)(b),
OxiEither::Right(e) => OxiEither::Right(e),
})
}
pub fn lift_fn<F: Fn(A) -> B + 'static>(f: F) -> Self {
Self::new(move |a| OxiEither::Left(f(a)))
}
}
#[allow(dead_code)]
pub struct SelectCombinator<E, A, B> {
pub lhs: OxiEither<A, E>,
pub rhs: OxiEither<Box<dyn Fn(A) -> B>, E>,
}
#[allow(dead_code)]
impl<E: Clone, A, B> SelectCombinator<E, A, B> {
pub fn new(lhs: OxiEither<A, E>, rhs: OxiEither<Box<dyn Fn(A) -> B>, E>) -> Self {
Self { lhs, rhs }
}
pub fn select(self) -> OxiEither<B, E> {
match self.lhs {
OxiEither::Left(a) => match self.rhs {
OxiEither::Left(f) => OxiEither::Left(f(a)),
OxiEither::Right(e) => OxiEither::Right(e),
},
OxiEither::Right(e) => OxiEither::Right(e),
}
}
pub fn select_right_law(e: E) -> OxiEither<B, E> {
OxiEither::Right(e)
}
}
pub struct EitherLeftIter<A: Clone, B> {
pub(super) inner: Option<OxiEither<A, B>>,
}
impl<A: Clone, B> EitherLeftIter<A, B> {
pub fn new(e: OxiEither<A, B>) -> Self {
Self { inner: Some(e) }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum OxiEither<A, B> {
Left(A),
Right(B),
}
impl<A, B> OxiEither<A, B> {
pub fn is_left(&self) -> bool {
matches!(self, OxiEither::Left(_))
}
pub fn is_right(&self) -> bool {
matches!(self, OxiEither::Right(_))
}
pub fn map_left<C, F: FnOnce(A) -> C>(self, f: F) -> OxiEither<C, B> {
match self {
OxiEither::Left(a) => OxiEither::Left(f(a)),
OxiEither::Right(b) => OxiEither::Right(b),
}
}
pub fn map_right<C, F: FnOnce(B) -> C>(self, f: F) -> OxiEither<A, C> {
match self {
OxiEither::Left(a) => OxiEither::Left(a),
OxiEither::Right(b) => OxiEither::Right(f(b)),
}
}
pub fn fold<C, FL: FnOnce(A) -> C, FR: FnOnce(B) -> C>(self, fl: FL, fr: FR) -> C {
match self {
OxiEither::Left(a) => fl(a),
OxiEither::Right(b) => fr(b),
}
}
pub fn swap(self) -> OxiEither<B, A> {
match self {
OxiEither::Left(a) => OxiEither::Right(a),
OxiEither::Right(b) => OxiEither::Left(b),
}
}
pub fn left_or(self, default: A) -> A {
match self {
OxiEither::Left(a) => a,
OxiEither::Right(_) => default,
}
}
pub fn right_or(self, default: B) -> B {
match self {
OxiEither::Left(_) => default,
OxiEither::Right(b) => b,
}
}
pub fn left(self) -> Option<A> {
match self {
OxiEither::Left(a) => Some(a),
OxiEither::Right(_) => None,
}
}
pub fn right(self) -> Option<B> {
match self {
OxiEither::Left(_) => None,
OxiEither::Right(b) => Some(b),
}
}
pub fn as_left(&self) -> Option<&A> {
match self {
OxiEither::Left(a) => Some(a),
OxiEither::Right(_) => None,
}
}
pub fn as_right(&self) -> Option<&B> {
match self {
OxiEither::Left(_) => None,
OxiEither::Right(b) => Some(b),
}
}
}
impl<T> OxiEither<T, T> {
pub fn merge<F: FnOnce(T) -> T>(self, f: F) -> T {
match self {
OxiEither::Left(a) | OxiEither::Right(a) => f(a),
}
}
pub fn into_inner(self) -> T {
match self {
OxiEither::Left(a) | OxiEither::Right(a) => a,
}
}
}
pub struct EitherRightIter<A, B> {
pub(super) inner: Option<OxiEither<A, B>>,
}
impl<A, B: Clone> EitherRightIter<A, B> {
pub fn new(e: OxiEither<A, B>) -> Self {
Self { inner: Some(e) }
}
}
#[allow(dead_code)]
pub struct EitherPartition<L, R> {
pub lefts: Vec<L>,
pub rights: Vec<R>,
}
#[allow(dead_code)]
impl<L, R> EitherPartition<L, R> {
pub fn from_iter<I: IntoIterator<Item = OxiEither<L, R>>>(iter: I) -> Self {
let mut lefts = Vec::new();
let mut rights = Vec::new();
for item in iter {
match item {
OxiEither::Left(l) => lefts.push(l),
OxiEither::Right(r) => rights.push(r),
}
}
Self { lefts, rights }
}
pub fn total(&self) -> usize {
self.lefts.len() + self.rights.len()
}
pub fn no_lefts(&self) -> bool {
self.lefts.is_empty()
}
pub fn no_rights(&self) -> bool {
self.rights.is_empty()
}
pub fn left_ratio(&self) -> f64 {
let total = self.total();
if total == 0 {
0.0
} else {
self.lefts.len() as f64 / total as f64
}
}
}
pub struct RightIter<A, B, I: Iterator<Item = OxiEither<A, B>>> {
pub(super) inner: I,
}
pub struct LeftIter<A, B, I: Iterator<Item = OxiEither<A, B>>> {
pub(super) inner: I,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TripleSum<A, B, C> {
First(A),
Second(B),
Third(C),
}
impl<A, B, C> TripleSum<A, B, C> {
pub fn to_nested(self) -> OxiEither<A, OxiEither<B, C>> {
match self {
TripleSum::First(a) => OxiEither::Left(a),
TripleSum::Second(b) => OxiEither::Right(OxiEither::Left(b)),
TripleSum::Third(c) => OxiEither::Right(OxiEither::Right(c)),
}
}
pub fn is_first(&self) -> bool {
matches!(self, TripleSum::First(_))
}
pub fn is_second(&self) -> bool {
matches!(self, TripleSum::Second(_))
}
pub fn is_third(&self) -> bool {
matches!(self, TripleSum::Third(_))
}
}
#[allow(dead_code)]
pub struct EitherTraversal<A, B> {
pub successes: Vec<B>,
pub error: Option<A>,
}
#[allow(dead_code)]
impl<A, B> EitherTraversal<A, B> {
pub fn new() -> Self {
Self {
successes: Vec::new(),
error: None,
}
}
pub fn step(&mut self, value: OxiEither<B, A>) {
if self.error.is_some() {
return;
}
match value {
OxiEither::Left(b) => self.successes.push(b),
OxiEither::Right(a) => self.error = Some(a),
}
}
pub fn finish(self) -> OxiEither<Vec<B>, A> {
match self.error {
None => OxiEither::Left(self.successes),
Some(e) => OxiEither::Right(e),
}
}
pub fn has_error(&self) -> bool {
self.error.is_some()
}
pub fn success_count(&self) -> usize {
self.successes.len()
}
}
#[derive(Clone, Debug)]
pub struct EitherIter<A, B> {
inner: OxiEither<A, B>,
done: bool,
}
impl<A: Clone, B: Clone> EitherIter<A, B> {
pub fn new(e: OxiEither<A, B>) -> Self {
Self {
inner: e,
done: false,
}
}
}