type_fn/
lib.rs

1#![no_std]
2
3/// Default trait for returning something from a type-fn
4pub trait TypeFn {
5    type Ret;
6}
7
8/// Calls a type-fn
9#[macro_export]
10macro_rules! call {
11    ($($fn:tt)+) => {
12        <$($fn)+ as TypeFn>::Ret
13    };
14}
15
16#[macro_export]
17macro_rules! call_as {
18    ($fty:ty => $($fn:tt)+) => {
19        <$($fn)+ as $fty>::Ret
20    };
21}
22
23/// Verifies equality between two types at compile-time.
24#[macro_export]
25macro_rules! assert_types_eq {
26    ($a:ty, $b:ty) => {{
27        let _: ::core::marker::PhantomData<$a> = ::core::marker::PhantomData::<$b>;
28    }};
29}
30
31/// Generates type-fn implementations.
32/// Syntax:
33/// `fn<$FnType$> $name$<$args$> $[$where-clause$]$ => $return-type$;`
34///
35/// FnType should simply be a trait containing `type Ret`, e.g. [`TypeFn`].
36///
37/// args is a type-arg list. To pattern-match types, use `T => Successor<T>` to
38/// implement something like like `Add<Successor<T>, Rhs>`.
39/// When implementing something for a specific type, use ` => TypeHere` (leave out
40/// the type arg name).
41/// If you need an extra type arg that wont be in the resulting type anywhere,
42/// add a bar `|` and write type args there as normal.
43///
44/// The where clause is just like rust's, except you need to put a plus before the first
45/// trait bound as well. Trailing commas are also mandatory.
46/// Trait bounds can only be set using the where-clause.
47///
48/// The return type can use any of the type arguments.
49///
50/// You can define an arbitrary amount of functions in one macro invocation.
51#[macro_export]
52macro_rules! type_fn_impl {
53    {@a_or_else_b  => } => {compiler_error!()};
54    {@a_or_else_b  => $($b:ident)+} => {$($b)*};
55    {@a_or_else_b $($a:ty)+ => $($b:ident)*} => { $($a)+ };
56    {$(fn $name:ident <$($($arg:ident)? $(=> $($argv:ty)+)?),* $(| $($targ:ident),+)?>
57        $(where $($tv:ty : $( + $( ?$tcqm:ident )? $( $tc:ident )? )+ ,)+)? => $ret:ty;)+}
58    => {
59        $(
60            impl<$($($arg, )?)* $($($targ, )*)?>
61                TypeFn for $name <$($crate::type_fn_impl!(@a_or_else_b $($($argv)*)? => $($arg)?)),*>
62            $(where $($tv : $($(?$tcqm)? $($tc)? + )+ ),+)?
63            {
64                type Ret = $ret;
65            }
66        )+
67    };
68}
69
70/// Creates type functions. You will still need to implement them yourself, e.g. using
71/// [`type_fn_impl!`].
72/// Syntax:
73/// `$[$visibility$]$ fn $name$ <$args$>;`
74///
75/// visibility is just like rust's normal visibility modifier.
76///
77/// args is a list of type arguments. They can not have constraints at this time.
78#[macro_export]
79macro_rules! type_fn {
80    ($($vis:vis fn $name:ident <$($arg:ident),*>;)*) => {
81        $(
82            #[derive(Default, Debug)]
83            $vis struct $name <$($arg),*> ($(::core::marker::PhantomData<$arg>, )*);
84        )*
85    };
86}
87
88#[cfg(test)]
89mod tests {
90    extern crate std;
91    use core::marker::PhantomData;
92
93    use crate::TypeFn;
94
95    #[test]
96    fn test_compile() {
97        #[derive(Default, Debug)]
98        struct Zero;
99        #[derive(Default, Debug)]
100        struct Succ<T>(PhantomData<T>);
101        type_fn! {
102            fn Add<Lhs, Rhs>;
103            fn Sub<Lhs, Rhs>;
104            fn Mul<Lhs, Rhs>;
105        }
106        type_fn_impl! {
107            fn Add< => Zero, Rhs> => Rhs;
108            fn Add<N => Succ<N>, Rhs>
109                where
110                    Add<N, Rhs>: + TypeFn,
111                => Succ<<Add<N, Rhs> as TypeFn>::Ret>;
112
113            fn Sub<Lhs, => Zero> => Lhs;
114            fn Sub<Lhs => Succ<Lhs>, Rhs => Succ<Rhs>>
115                where
116                    Sub<Lhs, Rhs> : + TypeFn,
117                => <Sub<Lhs, Rhs> as TypeFn>::Ret;
118
119            fn Mul< => Zero, Rhs> => Zero;
120            fn Mul<Lhs => Succ<Lhs>, Rhs>
121                where
122                    Mul<Lhs, Rhs>: + TypeFn,
123                    Add<Rhs, <Mul<Lhs, Rhs> as TypeFn>::Ret>: + TypeFn,
124                => <Add<Rhs, <Mul<Lhs, Rhs> as TypeFn>::Ret> as TypeFn>::Ret;
125        }
126
127        type TwoMinusOne = <Sub<Succ<Succ<Zero>>, Succ<Zero>> as TypeFn>::Ret;
128        std::println!("{:?}", <Sub<Succ<Succ<Zero>>, Succ<Zero>>>::default());
129        assert_types_eq!(TwoMinusOne, Succ<Zero>);
130        assert_types_eq!(call!(Sub<TwoMinusOne, Succ<Zero>>), Zero);
131        assert_types_eq!(
132            Succ<Succ<Succ<Succ<Zero>>>>,
133            <Mul<Succ<Succ<Zero>>, Succ<Succ<Zero>>> as TypeFn>::Ret
134        );
135        assert_types_eq!(Zero, <Mul<Succ<Succ<Zero>>, Zero> as TypeFn>::Ret);
136    }
137}