#![no_std]
pub trait TypeFn {
type Ret;
}
#[macro_export]
macro_rules! call {
($($fn:tt)+) => {
<$($fn)+ as TypeFn>::Ret
};
}
#[macro_export]
macro_rules! call_as {
($fty:ty => $($fn:tt)+) => {
<$($fn)+ as $fty>::Ret
};
}
#[macro_export]
macro_rules! assert_types_eq {
($a:ty, $b:ty) => {{
let _: ::core::marker::PhantomData<$a> = ::core::marker::PhantomData::<$b>;
}};
}
#[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 $name:ident <$($($arg:ident)? $(=> $($argv:ty)+)?),* $(| $($targ:ident),+)?>
$(where $($tv:ty : $( + $( ?$tcqm:ident )? $( $tc:ident )? )+ ,)+)? => $ret:ty;)+}
=> {
$(
impl<$($($arg, )?)* $($($targ, )*)?>
TypeFn for $name <$($crate::type_fn_impl!(@a_or_else_b $($($argv)*)? => $($arg)?)),*>
$(where $($tv : $($(?$tcqm)? $($tc)? + )+ ),+)?
{
type Ret = $ret;
}
)+
};
}
#[macro_export]
macro_rules! type_fn {
($($vis:vis fn $name:ident <$($arg:ident),*>;)*) => {
$(
#[derive(Default, Debug)]
$vis struct $name <$($arg),*> ($(::core::marker::PhantomData<$arg>, )*);
)*
};
}
#[cfg(test)]
mod tests {
extern crate std;
use core::marker::PhantomData;
use crate::TypeFn;
#[test]
fn test_compile() {
#[derive(Default, Debug)]
struct Zero;
#[derive(Default, Debug)]
struct Succ<T>(PhantomData<T>);
type_fn! {
fn Add<Lhs, Rhs>;
fn Sub<Lhs, Rhs>;
fn Mul<Lhs, Rhs>;
}
type_fn_impl! {
fn Add< => Zero, Rhs> => Rhs;
fn Add<N => Succ<N>, Rhs>
where
Add<N, Rhs>: + TypeFn,
=> Succ<<Add<N, Rhs> as TypeFn>::Ret>;
fn Sub<Lhs, => Zero> => Lhs;
fn Sub<Lhs => Succ<Lhs>, Rhs => Succ<Rhs>>
where
Sub<Lhs, Rhs> : + TypeFn,
=> <Sub<Lhs, Rhs> as TypeFn>::Ret;
fn Mul< => Zero, Rhs> => Zero;
fn 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;
std::println!("{:?}", <Sub<Succ<Succ<Zero>>, Succ<Zero>>>::default());
assert_types_eq!(TwoMinusOne, Succ<Zero>);
assert_types_eq!(call!(Sub<TwoMinusOne, Succ<Zero>>), 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);
}
}