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