use super::MonadTransformer;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OptionT<M> {
inner: M,
}
impl<M> OptionT<M> {
#[inline]
pub fn new(inner: M) -> Self {
OptionT { inner }
}
#[inline]
pub fn run(self) -> M {
self.inner
}
#[inline]
pub fn inner_ref(&self) -> &M {
&self.inner
}
}
impl<A> OptionT<Option<Option<A>>> {
#[inline]
pub fn some_option(value: A) -> Self {
OptionT::new(Some(Some(value)))
}
#[inline]
pub fn none_option() -> Self {
OptionT::new(Some(None))
}
#[inline]
pub fn map<B, F>(self, f: F) -> OptionT<Option<Option<B>>>
where
F: FnOnce(A) -> B,
{
OptionT::new(self.inner.map(|opt| opt.map(f)))
}
#[inline]
pub fn flat_map<B, F>(self, f: F) -> OptionT<Option<Option<B>>>
where
F: FnOnce(A) -> OptionT<Option<Option<B>>>,
{
OptionT::new(match self.inner {
Some(Some(a)) => f(a).inner,
Some(None) => Some(None),
None => None,
})
}
#[inline]
pub fn apply<B, F>(self, f: OptionT<Option<Option<F>>>) -> OptionT<Option<Option<B>>>
where
F: FnOnce(A) -> B,
{
OptionT::new(match (self.inner, f.inner) {
(Some(Some(a)), Some(Some(func))) => Some(Some(func(a))),
(Some(None), _) | (_, Some(None)) => Some(None),
(None, _) | (_, None) => None,
})
}
}
impl<A> MonadTransformer for OptionT<Option<Option<A>>> {
type BaseMonad = Option<A>;
#[inline]
fn lift(base: Option<A>) -> Self {
OptionT::new(Some(base))
}
}
impl<A, E> OptionT<Result<Option<A>, E>> {
#[inline]
pub fn some(value: A) -> Self {
OptionT::new(Ok(Some(value)))
}
#[inline]
pub fn none() -> Self {
OptionT::new(Ok(None))
}
#[inline]
pub fn err(e: E) -> Self {
OptionT::new(Err(e))
}
#[inline]
pub fn lift_m(result: Result<A, E>) -> Self {
OptionT::new(result.map(Some))
}
#[inline]
pub fn map<B, F>(self, f: F) -> OptionT<Result<Option<B>, E>>
where
F: FnOnce(A) -> B,
{
OptionT::new(self.inner.map(|opt| opt.map(f)))
}
#[inline]
pub fn map_err<E2, F>(self, f: F) -> OptionT<Result<Option<A>, E2>>
where
F: FnOnce(E) -> E2,
{
OptionT::new(self.inner.map_err(f))
}
#[inline]
pub fn flat_map<B, F>(self, f: F) -> OptionT<Result<Option<B>, E>>
where
F: FnOnce(A) -> OptionT<Result<Option<B>, E>>,
{
OptionT::new(match self.inner {
Ok(Some(a)) => f(a).inner,
Ok(None) => Ok(None),
Err(e) => Err(e),
})
}
#[inline]
pub fn apply<B, F>(self, f: OptionT<Result<Option<F>, E>>) -> OptionT<Result<Option<B>, E>>
where
F: FnOnce(A) -> B,
{
OptionT::new(match (self.inner, f.inner) {
(Ok(Some(a)), Ok(Some(func))) => Ok(Some(func(a))),
(Ok(None), _) | (_, Ok(None)) => Ok(None),
(Err(e), _) | (_, Err(e)) => Err(e),
})
}
#[inline]
pub fn map2<B, C, F>(
self,
other: OptionT<Result<Option<B>, E>>,
f: F,
) -> OptionT<Result<Option<C>, E>>
where
F: FnOnce(A, B) -> C,
{
OptionT::new(match (self.inner, other.inner) {
(Ok(Some(a)), Ok(Some(b))) => Ok(Some(f(a, b))),
(Ok(None), _) | (_, Ok(None)) => Ok(None),
(Err(e), _) | (_, Err(e)) => Err(e),
})
}
#[inline]
pub fn or_else<F>(self, f: F) -> Self
where
F: FnOnce() -> Self,
{
OptionT::new(match self.inner {
Ok(Some(a)) => Ok(Some(a)),
Ok(None) => f().inner,
Err(e) => Err(e),
})
}
#[inline]
pub fn get_or_else<F>(self, default: F) -> Result<A, E>
where
F: FnOnce() -> A,
{
self.inner.map(|opt| opt.unwrap_or_else(default))
}
#[inline]
pub fn to_result(self) -> Result<Option<A>, E> {
self.inner
}
#[inline]
pub fn is_some(&self) -> bool {
matches!(&self.inner, Ok(Some(_)))
}
#[inline]
pub fn is_none(&self) -> bool {
matches!(&self.inner, Ok(None))
}
#[inline]
pub fn is_err(&self) -> bool {
self.inner.is_err()
}
}
impl<A, E> MonadTransformer for OptionT<Result<Option<A>, E>> {
type BaseMonad = Result<A, E>;
#[inline]
fn lift(base: Result<A, E>) -> Self {
OptionT::lift_m(base)
}
}
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
impl<A> OptionT<Vec<Option<A>>> {
#[inline]
pub fn some_vec(value: A) -> Self {
OptionT::new(alloc::vec![Some(value)])
}
#[inline]
pub fn none_vec() -> Self {
OptionT::new(alloc::vec![None])
}
#[inline]
pub fn from_vec(values: Vec<Option<A>>) -> Self {
OptionT::new(values)
}
#[inline]
pub fn map<B, F>(self, mut f: F) -> OptionT<Vec<Option<B>>>
where
F: FnMut(A) -> B,
{
OptionT::new(self.inner.into_iter().map(|opt| opt.map(&mut f)).collect())
}
#[inline]
pub fn flat_map<B, F>(self, mut f: F) -> OptionT<Vec<Option<B>>>
where
F: FnMut(A) -> OptionT<Vec<Option<B>>>,
{
let results: Vec<Option<B>> = self
.inner
.into_iter()
.flat_map(|opt| match opt {
Some(a) => f(a).inner,
None => alloc::vec![None],
})
.collect();
OptionT::new(results)
}
}
#[cfg(feature = "alloc")]
impl<A> MonadTransformer for OptionT<Vec<Option<A>>> {
type BaseMonad = Vec<A>;
#[inline]
fn lift(base: Vec<A>) -> Self {
OptionT::new(base.into_iter().map(Some).collect())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_option_t_some() {
let opt: OptionT<Result<Option<i32>, &str>> = OptionT::some(42);
assert_eq!(opt.run(), Ok(Some(42)));
}
#[test]
fn test_option_t_none() {
let opt: OptionT<Result<Option<i32>, &str>> = OptionT::none();
assert_eq!(opt.run(), Ok(None));
}
#[test]
fn test_option_t_err() {
let opt: OptionT<Result<Option<i32>, &str>> = OptionT::err("error");
assert_eq!(opt.run(), Err("error"));
}
#[test]
fn test_option_t_map() {
let opt: OptionT<Result<Option<i32>, &str>> = OptionT::some(21);
let result = opt.map(|x| x * 2);
assert_eq!(result.run(), Ok(Some(42)));
let none: OptionT<Result<Option<i32>, &str>> = OptionT::none();
let result_none = none.map(|x| x * 2);
assert_eq!(result_none.run(), Ok(None));
}
#[test]
fn test_option_t_flat_map() {
let opt: OptionT<Result<Option<i32>, &str>> = OptionT::some(10);
let result = opt.flat_map(|x| {
if x > 5 {
OptionT::some(x * 2)
} else {
OptionT::none()
}
});
assert_eq!(result.run(), Ok(Some(20)));
let opt2: OptionT<Result<Option<i32>, &str>> = OptionT::some(3);
let result2 = opt2.flat_map(|x| {
if x > 5 {
OptionT::some(x * 2)
} else {
OptionT::none()
}
});
assert_eq!(result2.run(), Ok(None));
}
#[test]
#[allow(clippy::type_complexity)] fn test_option_t_apply() {
let val: OptionT<Result<Option<i32>, &str>> = OptionT::some(21);
let func: OptionT<Result<Option<fn(i32) -> i32>, &str>> = OptionT::some(|x: i32| x * 2);
let result = val.apply(func);
assert_eq!(result.run(), Ok(Some(42)));
}
#[test]
fn test_option_t_lift_m() {
let ok: Result<i32, &str> = Ok(42);
let lifted: OptionT<Result<Option<i32>, &str>> = OptionT::lift_m(ok);
assert_eq!(lifted.run(), Ok(Some(42)));
let err: Result<i32, &str> = Err("error");
let lifted_err: OptionT<Result<Option<i32>, &str>> = OptionT::lift_m(err);
assert_eq!(lifted_err.run(), Err("error"));
}
#[test]
fn test_option_t_map2() {
let a: OptionT<Result<Option<i32>, &str>> = OptionT::some(10);
let b: OptionT<Result<Option<i32>, &str>> = OptionT::some(20);
let combined = a.map2(b, |x, y| x + y);
assert_eq!(combined.run(), Ok(Some(30)));
}
#[test]
fn test_option_t_or_else() {
let a: OptionT<Result<Option<i32>, &str>> = OptionT::none();
let b = || OptionT::some(42);
assert_eq!(a.or_else(b).run(), Ok(Some(42)));
}
#[test]
fn test_option_t_left_identity() {
let a = 5;
let f = |x: i32| OptionT::<Result<Option<i32>, &str>>::some(x * 2);
let left = OptionT::<Result<Option<i32>, &str>>::some(a).flat_map(f);
let right = f(a);
assert_eq!(left.run(), right.run());
}
#[test]
fn test_option_t_right_identity() {
let m: OptionT<Result<Option<i32>, &str>> = OptionT::some(42);
let result = m.flat_map(OptionT::some);
assert_eq!(result.run(), Ok(Some(42)));
}
#[test]
fn test_option_t_associativity() {
let m: OptionT<Result<Option<i32>, &str>> = OptionT::some(5);
let f = |x: i32| OptionT::<Result<Option<i32>, &str>>::some(x + 1);
let g = |x: i32| OptionT::<Result<Option<i32>, &str>>::some(x * 2);
let left = m.flat_map(f).flat_map(g);
let right = OptionT::<Result<Option<i32>, &str>>::some(5).flat_map(|x| f(x).flat_map(g));
assert_eq!(left.run(), right.run());
}
#[cfg(feature = "alloc")]
#[test]
fn test_option_t_vec() {
let opt: OptionT<Vec<Option<i32>>> = OptionT::some_vec(42);
assert_eq!(opt.run(), alloc::vec![Some(42)]);
let mapped = OptionT::some_vec(21).map(|x| x * 2);
assert_eq!(mapped.run(), alloc::vec![Some(42)]);
}
}