fp_library/classes/extract.rs
1//! Types that hold exactly one value which can be extracted.
2//!
3//! [`Extract`] is the inverse of [`Deferrable`](crate::classes::Deferrable): where
4//! `Deferrable` constructs a value lazily from a thunk, `Extract` forces/extracts
5//! the inner value. For types whose brand implements `Extract` (e.g.,
6//! [`ThunkBrand`](crate::brands::ThunkBrand)), `extract(defer(|| x)) == x`
7//! forms a round-trip. Note that `Extract` is a brand-level trait (implemented
8//! by `ThunkBrand`), while `Deferrable` is a value-level trait (implemented by
9//! concrete types like [`Thunk`](crate::types::Thunk)).
10//!
11//! This trait is used by [`Free::evaluate`](crate::types::Free::evaluate) to
12//! execute the effects in a [`Free`](crate::types::Free) monad.
13//!
14//! ### Examples
15//!
16//! ```
17//! use fp_library::{
18//! brands::*,
19//! functions::*,
20//! types::*,
21//! };
22//!
23//! let thunk = Thunk::new(|| 42);
24//! assert_eq!(extract::<ThunkBrand, _>(thunk), 42);
25//! ```
26
27#[fp_macros::document_module]
28mod inner {
29 use {
30 crate::kinds::*,
31 fp_macros::*,
32 };
33
34 /// A type containing exactly one extractable value, providing a natural
35 /// transformation `F ~> Id`.
36 ///
37 /// This trait witnesses that a type always holds a single value that can be
38 /// extracted by running its effect. It is used by
39 /// [`Free::evaluate`](crate::types::Free::evaluate) to execute the effects in a
40 /// [`Free`](crate::types::Free) monad.
41 ///
42 /// `Extract` is the inverse of [`Deferrable`](crate::classes::Deferrable):
43 /// `Deferrable` constructs lazy values from thunks, while `Extract` forces and
44 /// extracts them. For types whose brand implements `Extract` (e.g.,
45 /// `ThunkBrand`), the round-trip law `extract(defer(|| x)) == x` holds.
46 ///
47 /// Implemented by types that always contain exactly one value and can
48 /// surrender ownership of it. [`Lazy`](crate::types::Lazy) cannot implement
49 /// this trait because forcing it returns `&A` (a reference), not an owned `A`.
50 /// [`Trampoline`](crate::types::Trampoline) does not have a brand and therefore
51 /// cannot participate in HKT traits.
52 ///
53 /// # Laws
54 ///
55 /// **Pure-extract:** extracting a pure value returns the original value.
56 /// For any `x: A`:
57 ///
58 /// ```text
59 /// extract(pure(x)) == x
60 /// ```
61 ///
62 /// This law states that wrapping a value in the type and immediately
63 /// extracting it is the identity.
64 #[kind(type Of<'a, A: 'a>: 'a;)]
65 pub trait Extract {
66 /// Extracts the inner value.
67 #[document_signature]
68 ///
69 #[document_type_parameters(
70 "The lifetime of the value.",
71 "The type of the value inside the container."
72 )]
73 ///
74 #[document_parameters("The container to extract from.")]
75 ///
76 #[document_returns("The inner value.")]
77 #[document_examples]
78 ///
79 /// ```
80 /// use fp_library::{
81 /// brands::*,
82 /// functions::*,
83 /// types::*,
84 /// };
85 ///
86 /// let thunk = Thunk::new(|| 42);
87 /// assert_eq!(extract::<ThunkBrand, _>(thunk), 42);
88 /// ```
89 fn extract<'a, A: 'a>(
90 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
91 ) -> A;
92 }
93
94 /// Extracts the inner value.
95 ///
96 /// Free function version that dispatches to [the type class' associated function][`Extract::extract`].
97 #[document_signature]
98 ///
99 #[document_type_parameters(
100 "The lifetime of the value.",
101 "The extractable type.",
102 "The type of the value inside the container."
103 )]
104 ///
105 #[document_parameters("The container to extract from.")]
106 ///
107 #[document_returns("The inner value.")]
108 #[document_examples]
109 ///
110 /// ```
111 /// use fp_library::{
112 /// brands::*,
113 /// functions::*,
114 /// types::*,
115 /// };
116 ///
117 /// let thunk = Thunk::new(|| 42);
118 /// assert_eq!(extract::<ThunkBrand, _>(thunk), 42);
119 /// ```
120 pub fn extract<'a, F, A>(fa: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)) -> A
121 where
122 F: Extract,
123 A: 'a, {
124 F::extract(fa)
125 }
126}
127
128pub use inner::*;
129
130#[cfg(test)]
131mod tests {
132 use {
133 crate::{
134 brands::*,
135 functions::*,
136 types::*,
137 },
138 quickcheck_macros::quickcheck,
139 };
140
141 /// Extract pure-extract law: extract(pure(x)) == x.
142 #[quickcheck]
143 fn prop_extract_pure(x: i32) -> bool {
144 let fa = Thunk::pure(x);
145 extract::<ThunkBrand, _>(fa) == x
146 }
147}