1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#![no_std]

/// Default trait for returning something from a type-fn
pub trait TypeFn {
    type Ret;
}

/// Verifies equality between two types at compile-time.
#[macro_export]
macro_rules! assert_types_eq {
    ($a:ty, $b:ty) => {{
        let _: ::core::marker::PhantomData<$a> = ::core::marker::PhantomData::<$b>;
    }};
}

/// Generates type-fn implementations.
/// Syntax:
/// `fn<$FnType$> $name$<$args$> $[$where-clause$]$ => $return-type$;`
///
/// FnType should simply be a trait containing `type Ret`, e.g. [`TypeFn`].
///
/// args is a type-arg list. To pattern-match types, use `T => Successor<T>` to
/// implement something like like `Add<Successor<T>, Rhs>`.
/// When implementing something for a specific type, use ` => TypeHere` (leave out
/// the type arg name).
/// If you need an extra type arg that wont be in the resulting type anywhere,
/// add a bar `|` and write type args there as normal.
///
/// The where clause is just like rust's, except you need to put a plus before the first
/// trait bound as well. Trailing commas are also mandatory.
/// Trait bounds can only be set using the where-clause.
///
/// The return type can use any of the type arguments.
///
/// You can define an arbitrary amount of functions in one macro invocation.
#[macro_export]
macro_rules! type_fn_impl {
    (@a_or_else_b  => ) => {compiler_error!()};
    (@a_or_else_b  => $($b:ident)+) => {$($b)*};
    (@a_or_else_b $($a:ty)+ => $($b:ident)*) => { $($a)+ };
    ($(fn < $sup:ty > $name:ident <$($($arg:ident)? $(=> $($argv:ty)+)?),* $(| $($targ:ident),+)?>
        $(where $($tv:ty : $( + $( ?$tcqm:ident )? $( $tc:ident )? )+ ,)+)? => $ret:ty;)+)
    => {
        $(
            impl<$($($arg, )?)* $($($targ, )*)?>
                $sup for $name <$($crate::type_fn_impl!(@a_or_else_b $($($argv)*)? => $($arg)?)),*>
            $(where $($tv : $($(?$tcqm)? $($tc)? + )+ ),+)?
            {
                type Ret = $ret;
            }
        )+
    };
}

/// Creates type functions. You will still need to implement them yourself, e.g. using
/// [`type_fn_impl!`].
/// Syntax:
/// `$[$visibility$]$ fn $name$ <$args$>;`
///
/// visibility is just like rust's normal visibility modifier.
///
/// args is a list of type arguments. They can not have constraints at this time.
#[macro_export]
macro_rules! type_fn {
    ($($vis:vis fn $name:ident <$($arg:ident),*>;)*) => {
        $(
            $vis struct $name <$($arg),*> ($(::core::marker::PhantomData<$arg>, )*);
        )*
    };
}

#[cfg(test)]
mod tests {
    use core::marker::PhantomData;

    use crate::TypeFn;

    #[test]
    fn test_compile() {
        struct Zero;
        struct Succ<T>(PhantomData<T>);
        type_fn! {
            fn Add<Lhs, Rhs>;
            fn Sub<Lhs, Rhs>;
            fn Mul<Lhs, Rhs>;
        }
        type_fn_impl! {
            fn<TypeFn> Add< => Zero, Rhs> => Rhs;
            fn<TypeFn> Add<N => Succ<N>, Rhs>
                where
                    Add<N, Rhs>: + TypeFn,
                => Succ<<Add<N, Rhs> as TypeFn>::Ret>;

            fn<TypeFn> Sub<Lhs, => Zero> => Lhs;
            fn<TypeFn> Sub<Lhs => Succ<Lhs>, Rhs => Succ<Rhs>>
                where
                    Sub<Lhs, Rhs> : + TypeFn,
                => <Sub<Lhs, Rhs> as TypeFn>::Ret;

            fn<TypeFn> Mul< => Zero, Rhs> => Zero;
            fn<TypeFn> Mul<Lhs => Succ<Lhs>, Rhs>
                where
                    Mul<Lhs, Rhs>: + TypeFn,
                    Add<Rhs, <Mul<Lhs, Rhs> as TypeFn>::Ret>: + TypeFn,
                => <Add<Rhs, <Mul<Lhs, Rhs> as TypeFn>::Ret> as TypeFn>::Ret;
        }

        type TwoMinusOne = <Sub<Succ<Succ<Zero>>, Succ<Zero>> as TypeFn>::Ret;
        assert_types_eq!(TwoMinusOne, Succ<Zero>);
        assert_types_eq!(<Sub<TwoMinusOne, Succ<Zero>> as TypeFn>::Ret, Zero);
        assert_types_eq!(
            Succ<Succ<Succ<Succ<Zero>>>>,
            <Mul<Succ<Succ<Zero>>, Succ<Succ<Zero>>> as TypeFn>::Ret
        );
        assert_types_eq!(Zero, <Mul<Succ<Succ<Zero>>, Zero> as TypeFn>::Ret);
    }
}