Initial commit
This commit is contained in:
commit
ef8f15a2e0
4 changed files with 165 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
/Cargo.lock
|
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "type-fn"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "Allows for simpler coding of type-level logic, e.g. for type-number systems."
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tudbut/type-fn"
|
||||
|
||||
[dependencies]
|
37
README.md
Normal file
37
README.md
Normal file
|
@ -0,0 +1,37 @@
|
|||
# type-fn
|
||||
|
||||
type-fn allows you to more simply create logic at the type level.
|
||||
|
||||
## Example
|
||||
|
||||
Unsigned addition, subtraction, and multiplication:
|
||||
|
||||
```rs
|
||||
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;
|
||||
}
|
||||
```
|
117
src/lib.rs
Normal file
117
src/lib.rs
Normal file
|
@ -0,0 +1,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);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue