fp_library/
macros.rs

1//! Macros for generating higher-kinded type traits and implementations.
2//!
3//! These macros provide a systematic way to generate traits for different kind arities,
4//! allowing the library to work with types of varying complexity from concrete types
5//! (kind `*`) to type constructors (kinds `* -> *`, `* -> * -> *`, etc.).
6
7/// Generates a [`KindN` trait][crate::hkt::kinds] of a specific arity and its corresponding blanket implementation.
8///
9/// This macro creates traits that represent type-level applications for different kind arities.
10/// Each generated trait has an `Output` associated type that represents the concrete type
11/// produced when the brand is applied to the appropriate type parameters.
12///
13/// # Parameters
14///
15/// - `$KindN`: The name of the trait to generate (e.g., `Kind0`, `Kind1`, `Kind2`).
16/// - `$ApplyN`: The corresponding type alias name (e.g., `Apply0`, `Apply1`, `Apply2`).
17/// - `$kind_string`: A string representation of the kind (e.g., `"*"`, `"* -> *"`, `"* -> * -> *"`).
18/// - `$Generics`: A tuple of generic type parameters (e.g., `()`, `(A)`, `(A, B)`).
19#[macro_export]
20macro_rules! make_trait_kind {
21	(
22		$KindN:ident,
23		$ApplyN:ident,
24		$kind_string:literal,
25		()
26	) => {
27		#[doc = concat!(
28			"Trait for [brands][crate::brands] of [types][crate::types] of kind `",
29			$kind_string,
30			"`."
31		)]
32		pub trait $KindN {
33			type Output;
34		}
35
36		impl<Brand> Kind<()> for Brand
37		where
38			Brand: $KindN,
39		{
40			type Output = $ApplyN<Brand>;
41		}
42	};
43	(
44		$KindN:ident,
45		$ApplyN:ident,
46		$kind_string:literal,
47		($($Generics:ident),+)
48	) => {
49		#[doc = concat!(
50			"Trait for [brands][crate::brands] of [types][crate::types] of kind `",
51			$kind_string,
52			"`."
53		)]
54		pub trait $KindN<$($Generics),+> {
55			type Output;
56		}
57
58		impl<Brand, $($Generics),+> Kind<($($Generics,)+)> for Brand
59		where
60			Brand: $KindN<$($Generics),+>,
61		{
62			type Output = $ApplyN<Brand, $($Generics),+>;
63		}
64	};
65}
66
67/// Generates an [`ApplyN` type alias][crate::hkt::apply] of a specific arity.
68///
69/// This macro creates type aliases that simplify the usage of kind traits by providing
70/// a more convenient syntax for type applications. These aliases are used throughout
71/// the library to make type signatures more readable.
72///
73/// # Parameters
74///
75/// - `$KindN`: The kind trait name (e.g., `Kind0`, `Kind1`, `Kind2`).
76/// - `$ApplyN`: The name of the type alias to generate (e.g., `Apply0`, `Apply1`, `Apply2`).
77/// - `$kind_string`: A string representation of the kind (e.g., `"*"`, `"* -> *"`, `"* -> * -> *"`).
78/// - `$Generics`: A tuple of generic type parameters (e.g., `()`, `(A)`, `(A, B)`).
79#[macro_export]
80macro_rules! make_type_apply {
81	(
82		$KindN:ident,
83		$ApplyN:ident,
84		$kind_string:literal,
85		()
86	) => {
87		#[doc = concat!(
88			"Alias for [types][crate::types] of kind `",
89			$kind_string,
90			"`."
91		)]
92		pub type $ApplyN<Brand> = <Brand as $KindN>::Output;
93	};
94	(
95		$KindN:ident,
96		$ApplyN:ident,
97		$kind_string:literal,
98		($($Generics:ident),+)
99	) => {
100		#[doc = concat!(
101			"Alias for [types][crate::types] of kind `",
102			$kind_string,
103			"`."
104		)]
105		pub type $ApplyN<Brand, $($Generics),+> = <Brand as $KindN<$($Generics),+>>::Output;
106	};
107}
108
109/// Generates a [`BrandN` trait][crate::hkt::brands] of a specific arity and its corresponding blanket implementation.
110///
111/// This macro creates traits that provide `inject` and `project` methods which enable
112/// bi-directional conversion between concrete types and their higher-kinded representations.
113///
114/// # Parameters
115///
116/// - `$BrandN`: The name of the brand trait to generate (e.g., `Brand0`, `Brand1`, `Brand2`).
117/// - `$kind_string`: A string representation of the kind (e.g., `"*"`, `"* -> *"`, `"* -> * -> *"`).
118/// - `$Generics`: A tuple of generic type parameters (e.g., `()`, `(A)`, `(A, B)`).
119#[macro_export]
120macro_rules! make_trait_brand {
121	(
122		$BrandN:ident,
123		$kind_string:literal,
124		()
125	) => {
126		#[doc = concat!(
127			"[`BrandN` trait][crate::hkt::brands] for [types][crate::types] with kind `",
128			$kind_string,
129			"`."
130		)]
131		pub trait $BrandN<Concrete>
132		where
133			Self: Kind<()>,
134		{
135			fn inject(a: Concrete) -> Apply<Self, ()>;
136			fn project(a: Apply<Self, ()>) -> Concrete;
137		}
138
139		impl<Me, Concrete> Brand<Concrete, ()> for Me
140		where
141			Me: Kind<()> + $BrandN<Concrete>,
142		{
143			fn inject(a: Concrete) -> Apply<Self, ()> {
144				<Me as $BrandN<Concrete>>::inject(a)
145			}
146
147			fn project(a: Apply<Self, ()>) -> Concrete {
148				<Me as $BrandN<Concrete>>::project(a)
149			}
150		}
151	};
152	(
153		$BrandN:ident,
154		$kind_string:literal,
155		($($Generics:ident),+)
156	) => {
157		#[doc = concat!(
158			"[`BrandN` trait][crate::hkt::brands] for [types][crate::types] with kind `",
159			$kind_string,
160			"`."
161		)]
162		pub trait $BrandN<Concrete, $($Generics),+>
163		where
164			Self: Kind<($($Generics,)+)>,
165		{
166			fn inject(a: Concrete) -> Apply<Self, ($($Generics,)+)>;
167			fn project(a: Apply<Self, ($($Generics,)+)>) -> Concrete;
168		}
169
170		impl<Me, Concrete, $($Generics),+> Brand<Concrete, ($($Generics,)+)> for Me
171		where
172			Me: Kind<($($Generics,)+)> + $BrandN<Concrete, $($Generics),+>,
173		{
174			fn inject(a: Concrete) -> Apply<Self, ($($Generics,)+)> {
175				<Me as $BrandN<Concrete, $($Generics),+>>::inject(a)
176			}
177
178			fn project(a: Apply<Self, ($($Generics,)+)>) -> Concrete {
179				<Me as $BrandN<Concrete, $($Generics),+>>::project(a)
180			}
181		}
182	};
183}
184
185/// Generates a [brand type][crate::brands] and its [`BrandN` trait][crate::hkt::brands] implementation.
186///
187/// This macro creates a concrete brand struct and implements the appropriate kind and brand traits
188/// for it. It's the primary way to create brand types that connect concrete types with their
189/// higher-kinded representations, enabling them to work with typeclasses.
190///
191/// # Parameters
192///
193/// - `$Brand`: The name of the brand struct to generate (e.g., `StringBrand`, `OptionBrand`).
194/// - `$Concrete`: The concrete type this brand represents (e.g., `String`, `Option`).
195/// - `$KindN`: The kind trait to implement (e.g., `Kind0`, `Kind1`, `Kind2`).
196/// - `$BrandN`: The brand trait to implement (e.g., `Brand0`, `Brand1`, `Brand2`).
197/// - `$Generics`: A tuple of generic type parameters (e.g., `()`, `(A)`, `(A, B)`).
198#[macro_export]
199macro_rules! impl_brand {
200	(
201		$Brand:ident,
202		$Concrete:ident,
203		$KindN:ident,
204		$BrandN:ident,
205		()
206	) => {
207		#[doc = concat!(
208			"[Brand][crate::brands] for [`",
209			stringify!($Concrete),
210			"`]."
211		)]
212		pub struct $Brand;
213
214		impl $KindN for $Brand {
215			type Output = $Concrete;
216		}
217
218		impl $BrandN<$Concrete> for $Brand {
219			fn inject(a: $Concrete) -> Apply<Self, ()> {
220				a
221			}
222
223			fn project(a: Apply<Self, ()>) -> $Concrete {
224				a
225			}
226		}
227	};
228	(
229		$Brand:ident,
230		$Concrete:ident,
231		$KindN:ident,
232		$BrandN:ident,
233		($($Generics:ident),+)
234	) => {
235		#[doc = concat!(
236			"[Brand][crate::brands] for [`",
237			stringify!($Concrete),
238			"`]."
239		)]
240		pub struct $Brand;
241
242		impl<$($Generics),+> $KindN<$($Generics),+> for $Brand {
243			type Output = $Concrete<$($Generics),+>;
244		}
245
246		impl<$($Generics),+> $BrandN<$Concrete<$($Generics),+>, $($Generics,)+> for $Brand {
247			fn inject(a: $Concrete<$($Generics),+>) -> Apply<Self, ($($Generics,)+)> {
248				a
249			}
250
251			fn project(a: Apply<Self, ($($Generics,)+)>) -> $Concrete<$($Generics),+> {
252				a
253			}
254		}
255	}
256}