fp_library/brands.rs
1//! Brands represent higher-kinded (unapplied/partially-applied) forms of
2//! [types][crate::types], as opposed to concrete types, which are
3//! fully-applied.
4//!
5//! For example, [`VecBrand`] represents the higher-kinded type [`Vec`], whereas
6//! `Vec A`/`Vec<A>` is the concrete type where `Vec` has been applied to some
7//! generic type `A`.
8//!
9//! ### Examples
10//!
11//! ```
12//! use fp_library::{
13//! brands::*,
14//! functions::*,
15//! };
16//!
17//! let x = Some(5);
18//! let y = map::<OptionBrand, _, _>(|i| i * 2, x);
19//! assert_eq!(y, Some(10));
20//! ```
21
22use {
23 crate::{
24 classes::{
25 LazyConfig,
26 RefCountedPointer,
27 TryLazyConfig,
28 },
29 types::{
30 ArcLazyConfig,
31 RcLazyConfig,
32 },
33 },
34 std::marker::PhantomData,
35};
36
37pub mod optics;
38
39/// Brand for [`Arc`](std::sync::Arc) atomic reference-counted pointer.
40#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
41pub struct ArcBrand;
42
43/// Brand for [atomically reference-counted][std::sync::Arc]
44/// [closures][Fn] (`Arc<dyn Fn(A) -> B>`).
45///
46/// This type alias provides a way to construct and type-check [`Arc`](std::sync::Arc)-wrapped
47/// closures in a generic context.
48pub type ArcFnBrand = FnBrand<ArcBrand>;
49
50/// Brand for thread-safe [`ArcLazy`](crate::types::ArcLazy).
51pub type ArcLazyBrand = LazyBrand<ArcLazyConfig>;
52
53/// Brand for thread-safe [`ArcTryLazy`](crate::types::ArcTryLazy).
54pub type ArcTryLazyBrand<E> = TryLazyBrand<E, ArcLazyConfig>;
55
56/// An adapter that partially applies a `Bifunctor` to its first argument, creating a `Functor` over the second argument.
57///
58/// ### Examples
59///
60/// ```
61/// use fp_library::{
62/// brands::*,
63/// classes::functor::map,
64/// };
65///
66/// let x = Result::<i32, i32>::Ok(5);
67/// let y = map::<BifunctorFirstAppliedBrand<ResultBrand, i32>, _, _>(|s| s * 2, x);
68/// assert_eq!(y, Ok(10));
69/// ```
70#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
71pub struct BifunctorFirstAppliedBrand<Brand, A>(PhantomData<(Brand, A)>);
72
73/// An adapter that partially applies a `Bifunctor` to its second argument, creating a `Functor` over the first argument.
74///
75/// ### Examples
76///
77/// ```
78/// use fp_library::{
79/// brands::*,
80/// classes::functor::map,
81/// };
82///
83/// let x = Result::<i32, i32>::Err(5);
84/// let y = map::<BifunctorSecondAppliedBrand<ResultBrand, i32>, _, _>(|e| e * 2, x);
85/// assert_eq!(y, Err(10));
86/// ```
87#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
88pub struct BifunctorSecondAppliedBrand<Brand, B>(PhantomData<(Brand, B)>);
89
90/// Brand for [`CatList`](crate::types::CatList).
91///
92/// `CatList` is the catenable list that serves as the backbone of
93/// [`Free`](crate::types::Free) monad evaluation, providing O(1) append
94/// and amortized O(1) uncons for the "Reflection without Remorse" technique
95/// that makes `Free` stack-safe.
96#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
97pub struct CatListBrand;
98
99/// Brand for the [`Const`](crate::types::const_val::Const) functor.
100#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
101pub struct ConstBrand<R>(PhantomData<R>);
102
103/// Brand for [`ControlFlow`](core::ops::ControlFlow).
104///
105/// The type parameters are swapped relative to `ControlFlow<B, C>` so that
106/// the first HKT parameter is the continue (loop/state) value and the second
107/// is the break (done/result) value, matching `tail_rec_m` conventions.
108#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
109pub struct ControlFlowBrand;
110
111/// Brand for the partially-applied form of [`ControlFlow`](core::ops::ControlFlow) with the [`Break`](core::ops::ControlFlow::Break) type applied.
112///
113/// Fixes the `Break` (result) type, yielding a `Functor` over the `Continue`
114/// (continuation) type.
115#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
116pub struct ControlFlowBreakAppliedBrand<B>(PhantomData<B>);
117
118/// Brand for the partially-applied form of [`ControlFlow`](core::ops::ControlFlow) with the [`Continue`](core::ops::ControlFlow::Continue) type applied.
119///
120/// Fixes the `Continue` (continuation) type, yielding a `Functor` over the `Break`
121/// (result) type.
122#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
123pub struct ControlFlowContinueAppliedBrand<C>(PhantomData<C>);
124
125/// Generic function brand parameterized by reference-counted pointer choice.
126#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
127pub struct FnBrand<PtrBrand: RefCountedPointer>(PhantomData<PtrBrand>);
128
129/// Brand for [`Identity`](crate::types::Identity).
130#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
131pub struct IdentityBrand;
132
133/// Brand for [`Lazy`](crate::types::Lazy).
134///
135/// # Type Parameters
136///
137/// - `Config`: The memoization strategy, implementing [`LazyConfig`]. Use
138/// [`RcLazyConfig`] for single-threaded contexts
139/// or [`ArcLazyConfig`] for thread-safe contexts.
140#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
141pub struct LazyBrand<Config: LazyConfig>(PhantomData<Config>);
142
143/// Brand for [`Option`].
144#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
145pub struct OptionBrand;
146
147/// Brand for [`Pair`](crate::types::Pair).
148#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
149pub struct PairBrand;
150
151/// Brand for the partially-applied form of [`Pair`](crate::types::Pair) with the first value applied.
152#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
153pub struct PairFirstAppliedBrand<First>(PhantomData<First>);
154
155/// Brand for the partially-applied form of [`Pair`](crate::types::Pair) with the second value applied.
156#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
157pub struct PairSecondAppliedBrand<Second>(PhantomData<Second>);
158
159/// An adapter that partially applies a `Profunctor` to its first argument, creating a `Functor`.
160///
161/// ### Examples
162///
163/// ```
164/// use fp_library::{
165/// brands::*,
166/// classes::functor::map,
167/// };
168///
169/// let f = |x: i32| x + 1;
170/// let g = map::<ProfunctorFirstAppliedBrand<RcFnBrand, i32>, _, _>(
171/// |y: i32| y * 2,
172/// std::rc::Rc::new(f) as std::rc::Rc<dyn Fn(i32) -> i32>,
173/// );
174/// assert_eq!(g(10), 22); // (10 + 1) * 2 = 22
175/// ```
176#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
177pub struct ProfunctorFirstAppliedBrand<Brand, A>(PhantomData<(Brand, A)>);
178
179/// An adapter that partially applies a `Profunctor` to its second argument, creating a `Contravariant` functor.
180///
181/// ### Examples
182///
183/// ```
184/// use fp_library::{
185/// brands::*,
186/// classes::contravariant::contramap,
187/// };
188///
189/// let f = |x: i32| x > 5;
190/// let is_long_int = contramap::<ProfunctorSecondAppliedBrand<RcFnBrand, bool>, _, _>(
191/// |s: String| s.len() as i32,
192/// std::rc::Rc::new(f) as std::rc::Rc<dyn Fn(i32) -> bool>,
193/// );
194/// assert_eq!(is_long_int("123456".to_string()), true);
195/// assert_eq!(is_long_int("123".to_string()), false);
196/// ```
197#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
198pub struct ProfunctorSecondAppliedBrand<Brand, B>(PhantomData<(Brand, B)>);
199
200/// Brand for [`Rc`](`std::rc::Rc`) reference-counted pointer.
201#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
202pub struct RcBrand;
203
204/// Brand for [reference-counted][std::rc::Rc] [closures][Fn]
205/// (`Rc<dyn Fn(A) -> B>`).
206///
207/// This type alias provides a way to construct and type-check [`Rc`](`std::rc::Rc`)-wrapped
208/// closures in a generic context.
209pub type RcFnBrand = FnBrand<RcBrand>;
210
211/// Brand for single-threaded [`RcLazy`](crate::types::RcLazy).
212pub type RcLazyBrand = LazyBrand<RcLazyConfig>;
213
214/// Brand for single-threaded [`RcTryLazy`](crate::types::RcTryLazy).
215pub type RcTryLazyBrand<E> = TryLazyBrand<E, RcLazyConfig>;
216
217/// Brand for [`Result`].
218#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
219pub struct ResultBrand;
220
221/// Brand for the partially-applied form of [`Result`] with the [`Err`] type applied.
222///
223/// This brand forms a [`crate::classes::Functor`] and [`crate::classes::Monad`] over the success ([`Ok`]) type.
224#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
225pub struct ResultErrAppliedBrand<E>(PhantomData<E>);
226
227/// Brand for the partially-applied form of [`Result`] with the [`Ok`] type applied.
228///
229/// This brand forms a [`crate::classes::Functor`] and [`crate::classes::Monad`] over the error ([`Err`]) type.
230#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
231pub struct ResultOkAppliedBrand<T>(PhantomData<T>);
232
233/// Brand for [`SendThunk`](crate::types::SendThunk).
234///
235/// Thread-safe counterpart of [`ThunkBrand`]. The inner closure is `Send`,
236/// enabling deferred computation across thread boundaries.
237///
238/// # HKT limitations
239///
240/// `SendThunkBrand` does **not** implement [`Functor`](crate::classes::Functor),
241/// [`Monad`](crate::classes::Monad), or any other HKT type-class traits.
242/// Those traits accept closure parameters as `impl Fn`/`impl FnOnce` without a
243/// `Send` bound, so there is no way to guarantee that the closures passed to
244/// `map`, `bind`, etc. are safe to store inside a `Send` thunk. Use
245/// [`ThunkBrand`] when HKT polymorphism is needed, or work with `SendThunk`
246/// directly through its inherent methods.
247#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
248pub struct SendThunkBrand;
249
250/// Brand for [`Thunk`](crate::types::Thunk).
251///
252/// Note: This is for `Thunk<'a, A>`, NOT for `Trampoline<A>`.
253/// `Trampoline` cannot implement HKT traits due to its `'static` requirement.
254#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
255pub struct ThunkBrand;
256
257/// Brand for [`TryLazy`](crate::types::TryLazy).
258///
259/// # Type Parameters
260///
261/// - `E`: The error type for the fallible computation.
262/// - `Config`: The memoization strategy, implementing [`TryLazyConfig`]. Use
263/// [`RcLazyConfig`] for single-threaded contexts
264/// or [`ArcLazyConfig`] for thread-safe contexts.
265///
266/// # `'static` bound on `E`
267///
268/// The type parameter `E` requires `'static` in all HKT trait implementations.
269/// This is an inherent limitation of the Brand pattern's reliance on type erasure:
270/// the `Kind` trait's associated type `Of<'a, A>` introduces its own lifetime `'a`,
271/// so any type parameter baked into the brand must outlive all possible `'a`, which
272/// effectively requires `'static`. This prevents use with borrowed error types in
273/// HKT contexts.
274#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
275pub struct TryLazyBrand<E, Config: TryLazyConfig>(PhantomData<(E, Config)>);
276
277/// Brand for [`TryThunk`](crate::types::TryThunk) (Bifunctor).
278#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
279pub struct TryThunkBrand;
280
281/// Brand for [`TryThunk`](crate::types::TryThunk) with the error value applied (Functor over [`Ok`]).
282///
283/// # `'static` bound on `E`
284///
285/// The type parameter `E` requires `'static` in all HKT trait implementations.
286/// This is an inherent limitation of the Brand pattern's reliance on type erasure:
287/// the `Kind` trait's associated type `Of<'a, A>` introduces its own lifetime `'a`,
288/// so any type parameter baked into the brand must outlive all possible `'a`, which
289/// effectively requires `'static`. This prevents use with borrowed error types in
290/// HKT contexts.
291///
292/// # Note
293///
294/// There is no `TrySendThunkErrAppliedBrand` counterpart. `SendThunk` (and by
295/// extension `TrySendThunk`) cannot implement HKT traits like [`Functor`](crate::classes::Functor)
296/// because the HKT trait signatures lack `Send` bounds on their closure parameters.
297/// Without HKT support, partially-applied brands serve no purpose.
298#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
299pub struct TryThunkErrAppliedBrand<E>(PhantomData<E>);
300
301/// Brand for [`TryThunk`](crate::types::TryThunk) with the success value applied (Functor over [`Err`]).
302///
303/// # `'static` bound on `A`
304///
305/// The type parameter `A` requires `'static` in all HKT trait implementations.
306/// This is an inherent limitation of the Brand pattern's reliance on type erasure:
307/// the `Kind` trait's associated type `Of<'a, A>` introduces its own lifetime `'a`,
308/// so any type parameter baked into the brand must outlive all possible `'a`, which
309/// effectively requires `'static`. This prevents use with borrowed success types in
310/// HKT contexts.
311///
312/// # Note
313///
314/// There is no `TrySendThunkOkAppliedBrand` counterpart. See
315/// [`TryThunkErrAppliedBrand`] for the rationale.
316#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
317pub struct TryThunkOkAppliedBrand<A>(PhantomData<A>);
318
319/// Brand for `(A,)`, with A not applied.
320#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
321pub struct Tuple1Brand;
322
323/// Brand for `(First, Second)`, with neither `First` nor `Second` applied.
324#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
325pub struct Tuple2Brand;
326
327/// Brand for `(First, Second)`, with `First` applied (Functor over `Second`).
328#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
329pub struct Tuple2FirstAppliedBrand<First>(PhantomData<First>);
330
331/// Brand for `(First, Second)`, with `Second` applied (Functor over `First`).
332#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
333pub struct Tuple2SecondAppliedBrand<Second>(PhantomData<Second>);
334
335/// Brand for [`Vec`].
336#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
337pub struct VecBrand;