typewit/
type_fn.rs

1//! Type-level functions.
2//! 
3//! Type-level functions come in two flavors: 
4//! [injective](#injective), and [non-injective](#non-injective)
5//! 
6//! 
7//! # Injective
8//! 
9//! An injective function is any function `f` for which `a != b` implies `f(a) != f(b)`.
10//! <br>(For both injective and non-injective functions, `f(a) != f(b)` implies `a != b`)
11//! 
12//! The [`InjTypeFn`] trait encodes injective type-level functions,
13//! requiring the type to implement both [`TypeFn`] and [`RevTypeFn`].
14//! 
15//! 
16//! ### Example: injective function
17//!
18//! ```rust
19//! # use typewit::CallInjFn;
20//! #
21//! typewit::inj_type_fn!{
22//!     struct Upcast;
23//!     
24//!     impl u8 => u16;
25//!     impl u16 => u32;
26//!     impl u32 => u64;
27//!     impl u64 => u128;
28//! }
29//! let _: CallInjFn<Upcast, u8> = 3u16;
30//! let _: CallInjFn<Upcast, u16> = 5u32;
31//! ```
32//! 
33//! Because `Upcast` is injective, 
34//! it is possible to query the argument from the returned value:
35//! 
36//! ```rust
37//! # use typewit::UncallFn;
38//! #
39//! let _: UncallFn<Upcast, u16> = 3u8;
40//! let _: UncallFn<Upcast, u128> = 5u64;
41//! # 
42//! # typewit::inj_type_fn!{
43//! #     struct Upcast;
44//! #     
45//! #     impl u8 => u16;
46//! #     impl u16 => u32;
47//! #     impl u32 => u64;
48//! #     impl u64 => u128;
49//! # }
50//! ```
51//! 
52//! # Non-injective
53//! 
54//! The [`TypeFn`] trait allows implementors to be non-injective.
55//!
56//! ### Example: non-injective function
57//!
58//! ```rust
59//! typewit::type_fn!{
60//!     struct Bar;
61//!     
62//!     impl<T> Vec<T> => T;
63//!     impl<T> Box<T> => T;
64//! }
65//! ```
66//! `Bar` is *non*-injective because it maps both `Vec<T>` and `Box<T>` to `T`.
67//! 
68//! 
69//! [`TypeFn`]: crate::type_fn::TypeFn
70//! [`CallFn`]: crate::type_fn::CallFn
71//! 
72
73use core::marker::PhantomData;
74
75mod injective;
76
77pub use self::injective::*;
78
79pub(crate) use self::injective::simple_inj_type_fn;
80
81#[doc(no_inline)]
82pub use crate::inj_type_fn;
83
84#[doc(no_inline)]
85pub use crate::type_fn;
86
87
88/// A function that operates purely on the level of types.
89/// 
90/// These can be used in `typewit` to 
91/// [map the type arguments of `TypeEq`](crate::TypeEq::project).
92/// 
93/// Type-level functions can also be declared with the 
94/// [`type_fn`](macro@crate::type_fn) macro.
95/// 
96/// # Properties
97/// 
98/// These are properties about `TypeFn` implementors that users can rely on.
99/// 
100/// For any given `F: TypeFn<A> + TypeFn<B>` these hold:
101/// 
102/// 1. If `A == B`, then `CallFn<F, A> == CallFn<F, B>`.
103/// 2. If `CallFn<F, A> != CallFn<F, B>`, then `A != B`. 
104/// 
105/// # Examples
106/// 
107/// ### Manual Implementation
108/// 
109/// ```rust
110/// use typewit::{TypeFn, CallFn};
111/// 
112/// let string: CallFn<AddOutput<String>, &str> = "foo".to_string() +  ", bar";
113/// let _: String = string;
114/// assert_eq!(string, "foo, bar");
115/// 
116/// 
117/// struct AddOutput<Lhs>(core::marker::PhantomData<Lhs>);
118/// 
119/// // This part is optional,
120/// // only necessary to pass the function as a value, not just as a type.
121/// impl<Lhs> AddOutput<Lhs> {
122///     const NEW: Self = Self(core::marker::PhantomData);
123/// }
124/// 
125/// impl<Lhs, Rhs> TypeFn<Rhs> for AddOutput<Lhs>
126/// where
127///     Lhs: core::ops::Add<Rhs>
128/// {
129///     type Output = Lhs::Output;
130/// }
131/// ```
132/// 
133/// ### Macro-based Implementation
134/// 
135/// This example uses the [`type_fn`](macro@crate::type_fn) macro
136/// to declare the type-level function,
137/// and is otherwise equivalent to the manual one.
138/// 
139/// ```rust
140/// use typewit::CallFn;
141/// 
142/// let string: CallFn<AddOutput<String>, &str> = "foo".to_string() +  ", bar";
143/// let _: String = string;
144/// assert_eq!(string, "foo, bar");
145/// 
146/// typewit::type_fn! {
147///     struct AddOutput<Lhs>;
148/// 
149///     impl<Rhs> Rhs => Lhs::Output
150///     where Lhs: core::ops::Add<Rhs>
151/// }
152/// ```
153/// 
154#[cfg_attr(feature = "rust_1_83", diagnostic::on_unimplemented(
155    message = "{Self} is not a type-level function over `{T}`",
156))]
157pub trait TypeFn<T: ?Sized> {
158    /// The return value of the function
159    type Output: ?Sized;
160
161    /// Helper constant for adding asserts in the `TypeFn` impl;
162    const TYPE_FN_ASSERTS: () = ();
163}
164
165/// Calls the `F` [type-level function](TypeFn) with `T` as its argument.
166/// 
167/// For `F:`[`InjTypeFn<T>`](crate::InjTypeFn), it's better to 
168/// use [`CallInjFn`] instead of this type alias.
169/// 
170/// 
171/// # Example
172/// 
173/// ```rust
174/// use typewit::CallFn;
175/// use core::ops::Mul;
176/// 
177/// assert_eq!(mul(3u8, &5u8), 15u8);
178/// 
179/// fn mul<L, R>(l: L, r: R) -> CallFn<MulOutput<L>, R> 
180/// where
181///     L: core::ops::Mul<R>
182/// {
183///     l * r
184/// }
185/// 
186/// // Declares `struct MulOutput<Lhs>`,
187/// // a type-level function from `Rhs` to the return type of `Lhs * Rhs`.
188/// typewit::type_fn! {
189///     struct MulOutput<Lhs>;
190///
191///     impl<Rhs> Rhs => <Lhs as Mul<Rhs>>::Output
192///     where Lhs: core::ops::Mul<Rhs>
193/// }
194/// ```
195/// 
196pub type CallFn<F, T> = <F as TypeFn<T>>::Output;
197
198///////////////////////////////////////////////////////
199
200/// Type-level function from `T` to `&'a T`
201pub struct GRef<'a>(PhantomData<fn() -> &'a ()>);
202
203impl<'a> GRef<'a> {
204    /// Make a value of this type-level function
205    pub const NEW: Self = Self(PhantomData);
206}
207
208simple_inj_type_fn!{
209    impl['a, T: 'a + ?Sized] (T => &'a T) for GRef<'a>
210}
211
212////////////////
213
214/// Type-level function from `T` to `&'a mut T`
215pub struct GRefMut<'a>(PhantomData<fn() -> &'a mut ()>);
216
217impl<'a> GRefMut<'a> {
218    /// Make a value of this type-level function
219    pub const NEW: Self = Self(PhantomData);
220}
221
222simple_inj_type_fn!{
223    impl['a, T: 'a + ?Sized] (T => &'a mut T) for GRefMut<'a>
224}
225
226////////////////
227
228/// Type-level function from `T` to `Box<T>`
229#[cfg(feature = "alloc")]
230#[cfg_attr(feature = "docsrs", doc(cfg(feature = "alloc")))]
231pub struct GBox;
232
233#[cfg(feature = "alloc")]
234simple_inj_type_fn!{
235    impl[T: ?Sized] (T => alloc::boxed::Box<T>) for GBox
236}
237
238////////////////
239
240/// Type-level identity function
241pub struct FnIdentity;
242
243simple_inj_type_fn!{
244    impl[T: ?Sized] (T => T) for FnIdentity
245}
246
247////////////////
248
249/// Type-level function which implements `TypeFn` by delegating to `F` 
250/// 
251/// This is mostly a workaround to write `F: TypeFn<T>` bounds in Rust 1.57.0
252/// (trait bounds in `const fn`s were stabilized in Rust 1.61.0).
253///
254/// Because `Foo<F>: Trait`-style bounds unintentionally work in 1.57.0,
255/// this crate uses `Invoke<F>: TypeFn<T>` 
256/// when the `"rust_1_61"` feature is disabled,
257/// and `F: TypeFn<T>` when it is enabled.
258/// 
259pub struct Invoke<F>(PhantomData<fn() -> F>);
260
261impl<F> Copy for Invoke<F> {}
262
263impl<F> Clone for Invoke<F> {
264    fn clone(&self) -> Self {
265        *self
266    }
267}
268
269impl<F> Invoke<F> {
270    /// Constructs an `Invoke`
271    pub const NEW: Self = Self(PhantomData);
272}
273
274
275impl<F, T: ?Sized> TypeFn<T> for Invoke<F> 
276where
277    F: TypeFn<T>
278{
279    type Output = CallFn<F, T>;
280}
281
282impl<F, R: ?Sized> RevTypeFn<R> for Invoke<F> 
283where
284    F: RevTypeFn<R>,
285{
286    type Arg = UncallFn<F, R>;
287}
288
289
290////////////////////////////////////////////////////////////////////////////////
291
292impl<F, T: ?Sized> TypeFn<T> for PhantomData<F> 
293where
294    F: TypeFn<T>
295{
296    type Output = CallFn<F, T>;
297}
298
299impl<F, R: ?Sized> RevTypeFn<R> for PhantomData<F> 
300where
301    F: RevTypeFn<R>,
302{
303    type Arg = UncallFn<F, R>;
304}
305
306
307
308////////////////////////////////////////////////////////////////////////////////
309
310
311mod uses_const_marker {
312    use crate::const_marker::Usize;
313
314    /// TypeFn from `(T, Usize<N>)` to `[T; N]`
315    pub(crate) struct PairToArrayFn;
316
317    super::simple_inj_type_fn!{
318        impl[T, const N: usize] ((T, Usize<N>) => [T; N]) for PairToArrayFn
319    }
320} 
321
322pub(crate) use uses_const_marker::*;
323
324
325
326// This type alias makes it so that docs for newer Rust versions don't
327// show `Invoke<F>`, keeping the method bounds the same as in 1.0.0.
328#[cfg(not(feature = "rust_1_61"))]
329pub(crate) type InvokeAlias<F> = Invoke<F>;
330
331#[cfg(feature = "rust_1_61")]
332pub(crate) type InvokeAlias<F> = F;