Skip to content

Latest commit

 

History

History
203 lines (151 loc) · 5.25 KB

README.md

File metadata and controls

203 lines (151 loc) · 5.25 KB

Type Freak

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!

Usage

Put this line to your Cargo.toml. Note that the crate is still in alpha stage. Stabilized API is not guaranteed.

type-freak = "~0"

Examples

Compile-time guards and static assertions

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!!!
}

Typed list

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]

Functional interface

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
}

Trait-level Option

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

Auto-inferred counters

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();
}

License

The project licensed under MIT or Apache 2.0. Pick the one that suits you.