Skip to content
vic

jerry73204/typ

Experimental type level programming in Rust

jerry73204/typ.json
{
"createdAt": "2020-08-17T19:56:13Z",
"defaultBranch": "master",
"description": "Experimental type level programming in Rust",
"fullName": "jerry73204/typ",
"homepage": "",
"language": "Rust",
"name": "typ",
"pushedAt": "2021-07-19T10:24:32Z",
"stargazersCount": 41,
"topics": [],
"updatedAt": "2025-11-13T09:44:45Z",
"url": "https://github.com/jerry73204/typ"
}

[Book | Doc]

TYP enables you to write type operators, the functions that translates types, in Rust syntax.

It is re-design of willcrichton/Tyrade and was inspired by jerry73204/type-freak.

TYP adopts Rust-like syntax, where values become types, and types become trait bounds. The core concept is the type operator, which is a function that takes type arguments and produce types. Trait bounds are optionally added to input and output types.

fn TypeOperatorName<generic1, generic2>(type1: _, type2: Trait1 + Trait2) -> TraitBound { ... }
  • <generic1, generic2> lists the generic identifiers that helps disginguishing from public types.
  • type1 and type2 are input types composed of generics and public types.
  • type1: _ means the type has no trait bound.
  • The output trait bound fn() -> TraitBound is optional.

The snipplet demonstrates a simple type operator.

typ! {
use typenum::Unsigned;
fn Add<lhs, rhs>(lhs: Unsigned, rhs: Unsigned) -> Unsigned {
lhs + rhs
}
}

TYP provides first-class support to typenum. Integer literals are translated to typenum types. The following literals are understood by TYP.

  • Signed integers: 7 or 7i
  • Unsigned integers: 7u
  • Bits: true and false

Common binary and unary operators applies on types with appropriate traits. For example, A + B expands to <A as Add<B>>::Output.

typ! {
use typenum::{Integer, Bit};
fn IsOdd<value>(value: Integer) -> Bit {
if value % 2 == 1 {
true
} else {
false
}
}
}

Like normal Rust, the match syntax lets you match and unpack types. You can bind new generics on a pattern using #[generics(...)] attribute.

The example demonstrates a type operator that appends a type at the end of type-level list. It’s done by recursively unpack the list into Cons nodes and Nil end-of-list marker.

pub trait List {}
pub struct Cons<Head, Tail: List> { /* omit */ }
impl<Head, Tail: List> List for Cons<Head, Tail> {}
pub struct Nil;
impl List for Nil {}
typ! {
fn Append<input, value>(input: List, value: _) -> List {
match input {
#[generics(head, tail: List)]
Cons::<head, tail> => {
let new_tail = Append(tail, value);
Cons::<head, new_tail>
}
Nil => {
Cons::<value, Nil>
}
}
}
}

More advanced examples can be found in [tests/]!(tests) directory.

  • [if/else]!(tests/macro/if_.rs)
  • [match]!(tests/macro/match_.rs)
  • [binary GCD]!(tests/macro/recursion.rs)

MIT license. See [LICENSE.txt]!(LICENSE.txt).