fp_library/classes/ref_semimonad.rs
1//! Contexts supporting by-reference monadic sequencing via [`bind`](crate::functions::bind).
2//!
3//! Like [`Semimonad::bind`](crate::classes::Semimonad::bind), but the closure
4//! receives `&A` instead of `A`. This enables memoized types like
5//! [`Lazy`](crate::types::Lazy) to participate in monadic sequencing without
6//! giving up their cached value.
7//!
8//! ### Examples
9//!
10//! ```
11//! use fp_library::{
12//! brands::*,
13//! classes::*,
14//! types::*,
15//! };
16//!
17//! let lazy = RcLazy::pure(5);
18//! let result = LazyBrand::<RcLazyConfig>::ref_bind(&lazy, |x: &i32| {
19//! Lazy::<_, RcLazyConfig>::new({
20//! let v = *x;
21//! move || v * 2
22//! })
23//! });
24//! assert_eq!(*result.evaluate(), 10);
25//! ```
26
27#[fp_macros::document_module]
28mod inner {
29 use {
30 crate::kinds::*,
31 fp_macros::*,
32 };
33
34 /// A type class for contexts supporting by-reference monadic sequencing.
35 ///
36 /// The closure receives `&A` and decides what to do with it, including
37 /// whether to clone. No `Clone` bound is imposed by the trait.
38 #[kind(type Of<'a, A: 'a>: 'a;)]
39 pub trait RefSemimonad {
40 /// Sequences a computation using a reference to the contained value.
41 #[document_signature]
42 ///
43 #[document_type_parameters(
44 "The lifetime of the values.",
45 "The type of the value inside the context.",
46 "The type of the value in the resulting context."
47 )]
48 ///
49 #[document_parameters(
50 "The context containing the value.",
51 "A function that receives a reference to the value and returns a new context."
52 )]
53 ///
54 #[document_returns("A new context produced by the function.")]
55 #[document_examples]
56 ///
57 /// ```
58 /// use fp_library::{
59 /// brands::*,
60 /// classes::*,
61 /// types::*,
62 /// };
63 ///
64 /// let lazy = RcLazy::pure(5);
65 /// let result = LazyBrand::<RcLazyConfig>::ref_bind(&lazy, |x: &i32| {
66 /// Lazy::<_, RcLazyConfig>::new({
67 /// let v = *x;
68 /// move || v * 2
69 /// })
70 /// });
71 /// assert_eq!(*result.evaluate(), 10);
72 /// ```
73 fn ref_bind<'a, A: 'a, B: 'a>(
74 fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
75 f: impl Fn(&A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) + 'a,
76 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>);
77 }
78
79 /// Like [`ref_bind`](crate::functions::bind), but with the arguments flipped.
80 /// Collapses two nested layers of a by-ref semimonad into one.
81 ///
82 /// Equivalent to `ref_bind(mma, |ma| ma.clone())`.
83 #[document_signature]
84 #[document_type_parameters(
85 "The lifetime of the computation.",
86 "The brand of the semimonad.",
87 "The type of the value inside the nested semimonad."
88 )]
89 #[document_parameters("The doubly-wrapped semimonadic value.")]
90 #[document_returns("The singly-wrapped semimonadic value.")]
91 #[document_examples]
92 ///
93 /// ```
94 /// use fp_library::{
95 /// brands::*,
96 /// functions::{
97 /// explicit::join,
98 /// *,
99 /// },
100 /// types::*,
101 /// };
102 ///
103 /// let inner = RcLazy::pure(5);
104 /// let outer = RcLazy::new({
105 /// let inner = inner.clone();
106 /// move || inner.clone()
107 /// });
108 /// let result = join::<LazyBrand<RcLazyConfig>, _, _>(&outer);
109 /// assert_eq!(*result.evaluate(), 5);
110 /// ```
111 pub fn ref_join<'a, Brand: RefSemimonad, A: 'a>(
112 mma: &Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)>)
113 ) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
114 where
115 Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone, {
116 Brand::ref_bind(mma, |ma| ma.clone())
117 }
118}
119
120pub use inner::*;