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
use super::*;

/// Part of [`MonadFail`] responsible for Haskell's `fail`.
pub trait Fail<'a, E: 'a + Send>: WeakFunctor<'a> {
    /// Equivalent of Haskell's `fail`.
    fn fail<A: 'a + Send>(e: E) -> Self::F<A>;
}

/// Equivalent of Haskell's `MonadFail`. Auto-implemented for all [`Fail`]`+`[`Monad`].
///
/// <https://hackage.haskell.org/package/base-4.18.0.0/docs/Control-Monad.html>
pub trait MonadFail<'a, E: 'a + Send>: Monad<'a> + Fail<'a, E> {}

impl<'a, E: 'a + Send, T: Monad<'a> + Fail<'a, E>> MonadFail<'a, E> for T {}

/// Represents a (collection of) [Monad]\(s) that can hold any type of error.
pub trait MonadFailAny<'a>: 'a + Send {
    /// [`MonadFail`] for a specific error type.
    type W<E: 'a + Send>: MonadFail<'a, E>;

    /// Associated infallible [`Monad`].
    type T: Monad<'a>;

    fn unstuff<A: 'a + Send, E: 'a + Send>(
        wa: WrapE<'a, A, E, Self>,
    ) -> Wrap<'a, Result<A, E>, Self::T>;

    fn stuff<A: 'a + Send, E: 'a + Send>(
        fa: Wrap<'a, Result<A, E>, Self::T>,
    ) -> WrapE<'a, A, E, Self>;

    /// Equivalent of [`Result::map_err`].
    fn map_err<A: 'a + Send, E0: 'a + Send, E1: 'a + Send>(
        wa: WrapE<'a, A, E0, Self>,
        f: impl 'a + Send + FnOnce(E0) -> E1,
    ) -> WrapE<'a, A, E1, Self> {
        Self::bind_err(wa, |e0| Self::fail(f(e0)))
    }

    /// Equivalent of `catch`/`except`. To inject errors on success, see [`MonadFailAny::bind`].
    fn bind_err<A: 'a + Send, E0: 'a + Send, E1: 'a + Send>(
        wa: WrapE<'a, A, E0, Self>,
        f: impl 'a + Send + FnOnce(E0) -> WrapE<'a, A, E1, Self>,
    ) -> WrapE<'a, A, E1, Self> {
        Self::bind(wa, |result| match result {
            Ok(a) => Self::pure(a),
            Err(e0) => f(e0),
        })
    }

    /// Bind the result, an equivalent of `catch`/`except` with ability to "raise" extra errors
    /// on success too, unlike [`MonadFailAny::bind_err`].
    ///
    /// Note: Reasonably it is expected to lack fail semantics for the underlying result.
    /// Therefore the default implementation descends into the non-fail monad [`MonadFailAny::T`].
    fn bind<A: 'a + Send, B: 'a + Send, E0: 'a + Send, E1: 'a + Send>(
        wa: WrapE<'a, A, E0, Self>,
        f: impl 'a + Send + FnOnce(Result<A, E0>) -> WrapE<'a, B, E1, Self>,
    ) -> WrapE<'a, B, E1, Self> {
        Self::stuff(<Self::T as Monad>::bind(Self::unstuff(wa), |result| {
            Self::unstuff(f(result))
        }))
    }

    /// Equivalent of [`Monad::join`], flattening the errors.
    ///
    /// Note: default implementation doesn't depend on [`Monad::join`].
    fn join<A: 'a + Send, E0: 'a + Send, E1: 'a + Send>(
        wwa: WrapE<'a, WrapE<'a, A, E0, Self>, E1, Self>,
    ) -> WrapE<'a, A, Result<E0, E1>, Self> {
        Self::bind(wwa, |result| match result {
            Ok(wa) => Self::map_err(wa, Ok),
            Err(e1) => Self::fail(Err(e1)),
        })
    }

    /// Lift the error.
    fn rotate_out<A: 'a + Send, E0: 'a + Send, E1: 'a + Send>(
        wa: WrapE<'a, Result<A, E1>, E0, Self>,
    ) -> WrapE<'a, A, Result<E1, E0>, Self> {
        <Self::W<Result<E1, E0>> as Monad>::bind(Self::map_err(wa, Err), |fa| match fa {
            Ok(a) => Self::pure(a),
            Err(e) => Self::fail(Ok(e)),
        })
    }
}

pub type WrapE<'a, A, E, Fallible> = Wrap<'a, A, <Fallible as MonadFailAny<'a>>::W<E>>;

pub trait MonadFailAnyExt<'a>: MonadFailAny<'a> {
    fn pure<E: 'a + Send, A: 'a + Send>(a: A) -> WrapE<'a, A, E, Self> {
        <Self::W<E> as Pure>::pure(a)
    }

    fn fail<E: 'a + Send, A: 'a + Send>(e: E) -> WrapE<'a, A, E, Self> {
        <Self::W<E> as Fail<E>>::fail(e)
    }
}

impl<'a, Fallible: ?Sized + MonadFailAny<'a>> MonadFailAnyExt<'a> for Fallible {}