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/// 
154pub trait TypeFn<T: ?Sized> {
155    /// The return value of the function
156    type Output: ?Sized;
157
158    /// Helper constant for adding asserts in the `TypeFn` impl;
159    const TYPE_FN_ASSERTS: () = ();
160}
161
162/// Calls the `F` [type-level function](TypeFn) with `T` as its argument.
163/// 
164/// For `F:`[`InjTypeFn<T>`](crate::InjTypeFn), it's better to 
165/// use [`CallInjFn`] instead of this type alias.
166/// 
167/// 
168/// # Example
169/// 
170/// ```rust
171/// use typewit::CallFn;
172/// use core::ops::Mul;
173/// 
174/// assert_eq!(mul(3u8, &5u8), 15u8);
175/// 
176/// fn mul<L, R>(l: L, r: R) -> CallFn<MulOutput<L>, R> 
177/// where
178///     L: core::ops::Mul<R>
179/// {
180///     l * r
181/// }
182/// 
183/// // Declares `struct MulOutput<Lhs>`,
184/// // a type-level function from `Rhs` to the return type of `Lhs * Rhs`.
185/// typewit::type_fn! {
186///     struct MulOutput<Lhs>;
187///
188///     impl<Rhs> Rhs => <Lhs as Mul<Rhs>>::Output
189///     where Lhs: core::ops::Mul<Rhs>
190/// }
191/// ```
192/// 
193pub type CallFn<F, T> = <F as TypeFn<T>>::Output;
194
195///////////////////////////////////////////////////////
196
197/// Type-level function from `T` to `&'a T`
198pub struct GRef<'a>(PhantomData<fn() -> &'a ()>);
199
200impl<'a> GRef<'a> {
201    /// Make a value of this type-level function
202    pub const NEW: Self = Self(PhantomData);
203}
204
205simple_inj_type_fn!{
206    impl['a, T: 'a + ?Sized] (T => &'a T) for GRef<'a>
207}
208
209////////////////
210
211/// Type-level function from `T` to `&'a mut T`
212pub struct GRefMut<'a>(PhantomData<fn() -> &'a mut ()>);
213
214impl<'a> GRefMut<'a> {
215    /// Make a value of this type-level function
216    pub const NEW: Self = Self(PhantomData);
217}
218
219simple_inj_type_fn!{
220    impl['a, T: 'a + ?Sized] (T => &'a mut T) for GRefMut<'a>
221}
222
223////////////////
224
225/// Type-level function from `T` to `Box<T>`
226#[cfg(feature = "alloc")]
227#[cfg_attr(feature = "docsrs", doc(cfg(feature = "alloc")))]
228pub struct GBox;
229
230#[cfg(feature = "alloc")]
231simple_inj_type_fn!{
232    impl[T: ?Sized] (T => alloc::boxed::Box<T>) for GBox
233}
234
235////////////////
236
237/// Type-level identity function
238pub struct FnIdentity;
239
240simple_inj_type_fn!{
241    impl[T: ?Sized] (T => T) for FnIdentity
242}
243
244////////////////
245
246/// Type-level function which implements `TypeFn` by delegating to `F` 
247/// 
248/// This is mostly a workaround to write `F: TypeFn<T>` bounds in Rust 1.57.0
249/// (trait bounds in `const fn`s were stabilized in Rust 1.61.0).
250///
251/// Because `Foo<F>: Trait`-style bounds unintentionally work in 1.57.0,
252/// this crate uses `Invoke<F>: TypeFn<T>` 
253/// when the `"rust_1_61"` feature is disabled,
254/// and `F: TypeFn<T>` when it is enabled.
255/// 
256pub struct Invoke<F>(PhantomData<fn() -> F>);
257
258impl<F> Copy for Invoke<F> {}
259
260impl<F> Clone for Invoke<F> {
261    fn clone(&self) -> Self {
262        *self
263    }
264}
265
266impl<F> Invoke<F> {
267    /// Constructs an `Invoke`
268    pub const NEW: Self = Self(PhantomData);
269}
270
271
272impl<F, T: ?Sized> TypeFn<T> for Invoke<F> 
273where
274    F: TypeFn<T>
275{
276    type Output = CallFn<F, T>;
277}
278
279impl<F, R: ?Sized> RevTypeFn<R> for Invoke<F> 
280where
281    F: RevTypeFn<R>,
282{
283    type Arg = UncallFn<F, R>;
284}
285
286
287////////////////////////////////////////////////////////////////////////////////
288
289impl<F, T: ?Sized> TypeFn<T> for PhantomData<F> 
290where
291    F: TypeFn<T>
292{
293    type Output = CallFn<F, T>;
294}
295
296impl<F, R: ?Sized> RevTypeFn<R> for PhantomData<F> 
297where
298    F: RevTypeFn<R>,
299{
300    type Arg = UncallFn<F, R>;
301}
302
303
304
305////////////////////////////////////////////////////////////////////////////////
306
307
308mod uses_const_marker {
309    use crate::const_marker::Usize;
310
311    /// TypeFn from `(T, Usize<N>)` to `[T; N]`
312    pub(crate) struct PairToArrayFn;
313
314    super::simple_inj_type_fn!{
315        impl[T, const N: usize] ((T, Usize<N>) => [T; N]) for PairToArrayFn
316    }
317} 
318
319pub(crate) use uses_const_marker::*;
320
321
322
323// This type alias makes it so that docs for newer Rust versions don't
324// show `Invoke<F>`, keeping the method bounds the same as in 1.0.0.
325#[cfg(not(feature = "rust_1_61"))]
326pub(crate) type InvokeAlias<F> = Invoke<F>;
327
328#[cfg(feature = "rust_1_61")]
329pub(crate) type InvokeAlias<F> = F;