[−][src]Crate type_freak
This crate provides collections of runtime-free, type-level data structures and type operators.
The design of this crate promises these properties:
- Runtime-free: Most type operations are done in compile-time.
- No concrete data: Structs and enums contain at most PhantomData fields.
- Less writing: It tries to make use of type aliases to save some inks.
Basics
This crate provides type-level data structures categorized by traits, including Boolean, TList, KVList, Counter and Maybe. These types can be initialized by following:
use type_freak::{ TListType, KVListType, list::{LCons, LNil}, counter::{Current, Next}, maybe::{UnwrapOr, Just, Nothing}, }; use typenum::{True, False, consts::*}; type List1 = TListType![String, Option<i8>]; // impl TList trait type List2 = LCons<String, LCons<Option<i8>, LNil>>; // same as above type MaybeJust = Just<f32>; // impl Maybe type MaybeNothing = Nothing; // impl Maybe type BoolTrue = True; // impl Boolean type BoolFalse = False; // impl Boolean type Kv = KVListType![(isize, U0), (usize, U1)]; // impl KVList type Cnt = Next<Next<Next<Current>>>; // impl Counter
Each data structure is manipuated either by functors or type operators. The distinction is due to implementation contraints, and it is encourage to use functors at first. For example, you can UnwrapOr a type that implements Maybe.
use type_freak::maybe::{UnwrapOr, Just, Nothing}; type Outcome1 = UnwrapOr<Just<i8>, u8>; // Outcome1 ~= i8 type Outcome2 = UnwrapOr<Nothing, u8>; // Outcome2 ~= u8
Actually, UnwrapOr is an alias to apply UnwrapOrFunctor on a type. Hence, these statements are equivalent but have longer syntax.
use type_freak::{ maybe::{UnwrapOrFunctor, Just, Nothing}, functional::ApplyFunctor, }; type Outcome1 = ApplyFunctor<UnwrapOrFunctor<u8>, Just<i8>>; // Outcome1 ~= i8 type Outcome2 = ApplyFunctor<UnwrapOrFunctor<u8>, Nothing>; // Outcome1 ~= u8
Functoinal interface
Usage
Both Maybe and TList allows you to pass a functor for data manipulation. In this example, we pass AddOneFunctor that increases input typed integer by one.
use type_freak::{ TListType, list::LMap, maybe::{Just, Nothing}, numeric::AddOneFunctor, }; use typenum::consts::*; type Out1 = LMap<TListType![U3, U2, U5], AddOneFunctor>; // Out1 ~= TListType![U4. U3. U5] type Out2 = LMap<Just<U7>, AddOneFunctor>; // Out2 ~= Just<U8> type Out3 = LMap<Nothing, AddOneFunctor>; // Out3 ~= Nothing
TList also provides LFilter and LScan that acts like Iterator's filter and scan. Their detailed usage is out of scope here.
Roll your own Functor
You can define your own functor by implementing Functor trait on your type. Here we make a functor that boxes the input type.
use type_freak::functional::Functor; struct BoxFunctor; impl<Input> Functor<Input> for BoxFunctor { type Output = Box<Input>; }
Use ApplyFunctor to apply your functor on a type.
type Out = ApplyFunctor<BoxFunctor, String>; // Out ~= Box<String>
To make sure it works as expected, the crate has a special operator IfSameOutput to let us write static asserttion. If it does its job, the following code should compile without errors.
use type_freak::{ functional::{Functor, ApplyFunctor}, control::IfSameOutput }; struct BoxFunctor; impl<Input> Functor<Input> for BoxFunctor { type Output = Box<Input>; } type Out = ApplyFunctor<BoxFunctor, String>; // Out ~= Box<String> type Assert = IfSameOutput<(), Out, Box<String>>; fn assert() { let _: Assert = (); }
Functors vs type operators
Most operations in this crate are in by Functors, while a handful of them are type operators. To be exact,
- Functors are structs that implements Functor trait.
- Type operators are traits that has
Output
associated type.
They serves for the same purpose. Since recursive associated types only works on trait type operators, some of the operators have both a trait and a functor.
For example,
LInsertAtOp is a trait that inserts a new type to
TList. LInsertAtOpOutput
is type alias to its Output
associated type.
LInsertAtFunctor is the functor wrapping around
above trait, and LInsertAt is an alias to apply the functor.
Naming conventions
Each traits, structs and type aliases serves their own purpose. The naming of these primitives tell you the usage and how the outcome could be.
Traits as type operators
The name has pattern *Op
, such as FirstOfOp.
Most type operators have a Output
associated type to represent the outcome
of type transformation. You can obtain the outcome by trait casting. For example,
type Outcome = <TListType![i8, i16] as LLengthOp<>>::Output; // Outcome ~= U2
Due to the cumbersome syntax, most type operators have a corresponding type alias to capture the output. For example,
type Outcome = LLengthOpOutput<TListType![i8, i16]>; // Output ~= U2
Traits as markers
Marker traits are usually placed under marker
namespace. For example,
type_freak::list::marker::EmptyTList.
These traits are mostly shown in trait bounds.
Structs as functors
A functor is a special struct that implements Functor.
The name ends in *Functor
, such as LLengthFunctor.
It works like type operators, and it can be applied to Maybe
and TList. Most type operations have a functor interface,
either by wrapping around a type operator trait or by standalone definition.
The crate provides ApplyFunctor<Func, Input> type alias to apply a functor on a type. For example,
type Outcome = ApplyFunctor<LLengthFunctor, TListType![u8, u16]>; // Outcome ~= U2 // Same as `LLengthFunctor as Functor<TListType![u8, u16]>`
Also, type aliases are available to save the pen and ink.
type Outcome = LLength<TListType![u8, u16]>; // Outcome ~= U2
Type aliases as type operator aliases
Most alias names ends in *OpOutput
. For example, the name
LLengthOpOutput<List> is the alias of
<List as LLengthOp>::Output
.
Type aliases as functor aliases
It is a short hand syntax to apply a functor on a type. The name has no
special suffix. For example, LLength<List>
is alias of ApplyFunctor<LLengthFunctor, List>
.
Modules
boolean | Trait level boolean algebra. |
control | Compile-time guards and static assertions. |
counter | An counter trait that can be automatically inferred, usually used for traversal. |
functional | |
kvlist | A typed list of key-value pairs. |
list | Typed list that supports insertion, removal and look-up. |
maybe | Trait-level equivalences to Option. |
numeric | Numeric type operators and functors. |
tuple | Type operators for tuple types. |
Macros
KVListType | Builds a type that implements KVList. |
TListType | Builds a type that implements TList. |
TListTypeWithTail | Builds a type that implements TList with extra appending list. |