[][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.

This example is not tested
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,

This example is not tested
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,

This example is not tested
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,

This example is not tested
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.

This example is not tested
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

Functional primitives like Functor and Compose.

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.