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