The crate is a collection of typed data structures, trait operators and useful type aliases for Rust. It was introduced to support tch-typed-tensor project, which provides compile-time checked tensor type.
It reduces runtime computation to minimum by design. The DSTs are manipulated by trait operators. That is, with Rust's associated types and generics, we can build non-trivial types like lists and key-value map.
So far, the crate ships following features. It's still in alpha stage and I'm glad for contributions!
- TList: a typed list with arbitrary type as keys.
- KVList: like TList, with extra values.
- Boolean: typed boolean algebra.
- Maybe: a trait analogous to
std::option::Option
. - Counter: a convient type to build recursive trait operators.
- Functoinal primitives: provides
Map
,Compose
andApplicative
, etc. - Trait operators for tuple types
- Control flow: typed
If
,IfLess
,IfSame
and more for compile-time guards and static assertions.
Put this line to your Cargo.toml
. Note that the crate is still in alpha stage.
Stabilized API is not guaranteed.
type-freak = "~0"
To assert one typed integer is less than the other typed integer:
use typenum::consts::*;
use type_freak::control::IfLessOutput;
type Out1 = IfLessOutput<usize, U3, U5>; // U3 < U5 is true, thus Out1 ~= usize
type Out2 = IfLessOutput<usize, U5, U3>; // U5 < U5 is false
fn assert() {
let _: Out1 = 0; // Goes fine here.
let _: Out2 = 0; // Compile error!!!
}
We can make sure two generic parameters are of the same type by IfSame
trait bound.
use type_freak::control::IfSame;
fn guarded_function<Lhs, Rhs>() -> String
where
Lhs: IfSame<Lhs, Rhs>
{
"Yeeeeeee!".to_owned()
}
fn comile_me() {
let _ = guarded_function::<String, String>(); // fine
let _ = guarded_function::<String, u8>(); // Compile error!!!
}
The TList
type represents a list of arbitrary types. It can be constructed
by TListType!
macro. The crate ships a variety of traits as type operators to
manipuate the list structure.
use type_freak::{TListType, list::*};
type List1 = TListType![u8, u16, u32];
type List2 = LPrepend<List1, u64>;
// List2 ~= TListType![u64, u8, u16, u32]
type List3<Index1> = LRemoveAt<List2, u16, Index1>;
// List3<_> ~= TListType![u64, u8, u32]
type List4<Index1> = LAppend<List3<Index1>, f32>;
// List4 ~= TListType![u64, u8, u32, f32]
type List5<Index1, Index2> = LInsertAt<List4<Index1>, u8, f64, Index2>;
// List5 ~= TListType![u64, u8, f64, u32, f32]
You can map, filter or scan a TList
with existing maps in crate.
Also, it's allowed to roll your own map to manipulate the data with ease.
struct BoxMap;
impl<Input> Map<Input> for BoxMap {
type Output = Box<Input>;
}
type ListBefore = TListType![String, [i64; 7], isize, (), (f64, f32)];
type ListAfter = LMap<List3, BoxMap>;
type Assert = IfSameOutput<
(),
ListAfter,
TListType! {
Box<String>,
Box<[i64; 7]>,
Box<isize>,
Box<()>,
Box<(f64, f32)>
},
>;
fn assert() {
let _: Assert = (); // static assertion
}
The Maybe
is analogous to std's Option
.
use typenum::consts::*;
use type_freak::maybe::{Maybe, Just, Nothing};
type Opt1 = Just<U3>;
type Opt2 = Nothing;
type Val1 = Unwrap<Opt1>; // U3
type Val2 = UnwrapOr<Opt1, U0>; // U3
type Val3 = UnwrapOr<Opt2, U0>; // U0
The Counter
traits along with Next
and Current
types are handly
tools to build recursive type operators. The following demo implements
an trait that removes a specific type from TList
.
The example works by a termination step and recursive step, corresponding
to to impl blocks. Note that the Index
argument is necessary to let compiler
distinguish the signatures of two impl blocks. Otherwise, the compiler will
complain about conflicting implementations.
use type_freak::{
list::{TList, LCons, LNil},
counter::{Counter, Current, Next},
};
/* Definition */
pub trait LRemoveAt<Target, Index>
where
Index: Counter,
Self: TList,
Self::Output: TList,
{
type Output;
}
// termination step
impl<Target, Tail> LRemoveAt<Target, Current> for LCons<Target, Tail>
where
Tail: TList,
{
type Output = Tail;
}
// recursion step
impl<Target, Index, NonTarget, Tail> LRemoveAt<Target, Next<Index>> for LCons<NonTarget, Tail>
where
Index: Counter,
Tail: TList + LRemoveAt<Target, Index>,
{
type Output = LCons<NonTarget, <Tail as LRemoveAt<Target, Index>>::Output>;
}
/* Auto-inference example */
// Here SomeList is equivalent to TListType![u8, u32]
type SomeList<Index> = <TListType![u8, u16, u32] as LRemoveAt<u16, Index>>::Output;
// The Index argument can be inferred by compiler
fn auto_inference() {
let _ = SomeList::<_>::new();
}
The project licensed under MIT or Apache 2.0. Pick the one that suits you.