• Stars
    star
    305
  • Rank 136,879 (Top 3 %)
  • Language
    Rust
  • Created over 4 years ago
  • Updated about 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A pure functional language for type-level programming in Rust

Tyrade: a pure functional language for type-level programming in Rust

ci

Tyrade is a proof-of-concept language showing how Rust traits enable a general-purpose type-level programming model. Its goal is to show that type-level programming is possible for useful tasks (not just writing Turing machines), and that programs can be written in a reasonable way. Here's what the language looks like:

tyrade! {
  // A type-level enum for Peano numerals, with "Z" (zero) and "S(n)" (successor).
  enum TNum {
    Z,
    S(TNum)
  }

  // A function that adds two Peano numerals together.
  fn TAdd<N1, N2>() {
    match N1 {
      Z => N2,
      S(N3) => TAdd(N3, S(N2))
    }
  }
}

fn num_tests() {
  // 1 + 1 == 2
  assert_type_eq::<S<S<Z>>, TAdd<S<Z>, S<Z>>>();
}

At its core, Tyrade supports recursive enums and pure recursive functions. For the main ideas behind Tyrade, continue below or consider reading my blog post on type-level programming: https://willcrichton.net/notes/type-level-programming/

Motivating example: security types

Others have shown that Rust traits are Turing-complete and can be used for e.g. Fizz-Buzz. However, the direct expression of type-level programs in traits is quite obtuse, i.e. the relationship between the conceptual program and the actual traits is hard to see.

As a simple example, consider two types HighSec and LowSec representing the security of an item:

struct High;
struct Low;

struct Item<T, Sec> {
  t: T,
  _sec: PhantomData<Sec>
}

A simple type-level program is to compute the maximum of two security levels S1 and S2. That is, if S1 = S2 = Low, then return Low, else return High. To encode this program in Rust traits, we turn the MaxLevel function into a trait, with an impl for each condition.

trait ComputeMaxLevel<Other> {
  type Output;
}

// These impls define the core computation
impl ComputeMaxLevel<Low>  for Low  { type Output = Low;  }
impl ComputeMaxLevel<High> for Low  { type Output = High; }
impl ComputeMaxLevel<Low>  for High { type Output = High; }
impl ComputeMaxLevel<High> for High { type Output = High; }

// The type alias gives us a more convenient way to "call" the type operator
type MaxLevel<L, R> = <L as ComputeMaxLevel<R>>::Output;

fn sec_tests() {
  // example unit tests
  assert_type_eq::<Low,  MaxLevel<Low, Low>>();
  assert_type_eq::<High, MaxLevel<Low, High>>();
}

The goal of Tyrade is to perform this translation automatically from a functional programming model. Using Tyrade, this program is written as:

tyrade!{
  enum Security {
    Low,
    High
  }

  fn MaxLevel<S1, S2>() {
    match S1 {
      Low => match S2 {
        Low => Low,
        High => High
      }
      High => High
    }
  }

  // In the high-level language, we can more easily see a chance for simplification.
  fn MaxLevel2<S1, S2>() {
    match S1 {
      Low => S2,
      High => High
    }
  }
}

This way, both the type definition and the type-level program are expressed using familiar constructs like fn, enum, and match.

More complex example: session and list types

Tyrade can be used to define a framework for communication protocols, e.g. session types. For example, the session types and their duals can be defined as follows:

tyrade! {
  enum SessionType {
    Close,
    Recv(Type, SessionType),
    Send(Type, SessionType),
    Choose(SessionType, SessionType),
    Offer(SessionType, SessionType),
    Label(SessionType),
    Goto(TNum)
  }

  fn Dual<S>() {
    match S {
      Close => S,
      Recv(T, S2) => Send(T, Dual(S2)),
      Send(T, S2) => Recv(T, Dual(S2)),
      Choose(S2, S3) => Offer(Dual(S2), Dual(S3)),
      Offer(S2, S3) => Choose(Dual(S2), Dual(S3)),
      Label(S2) => Label(Dual(S2)),
      Goto(N) => S
    }
  }
}

fn session_type_test() {
  // The dual of a Send is a Recv
  assert_type_eq::<
    Dual<Send<i32, Close>>,
    Recv<i32, Close>    
  >();
}

Tyrade provides a standard library of type-level building blocks like booleans, numbers, and lists. For example, we can use lists to implement the compile-time saving and indexing of jump points in session types.

struct Chan<Env, S>(PhantomData<(Env, S)>);

impl<Env: TList, S: SessionType> Chan<Env, Label<S>> {
  // label() pushes a type S onto the environment
  fn label(self) -> Chan<Cons<S, Env>, S> {
    Chan(PhantomData)
  }
}

impl<Env: TList, N: TNum> Chan<Env, Goto<N>>
where Env: ComputeTListNth<N> + ComputeTListSkip<N>
{
  // goto<N> gets the Nth type from the environment, removing every type
  // before then
  fn goto(self) -> Chan<TListSkip<Env, N>, TListNth<Env, N>> {
    Chan(PhantomData)
  }
}


fn session_type_test() {
  let c: Chan<
      Cons<Close, Nil>,
      Label<Goto<S<Z>>>> = Chan(PhantomData);

  // label() pushes Goto onto the Env list
  let c: Chan<
      Cons<Goto<S<Z>>, Cons<Close, Nil>>,
      Goto<S<Z>>> = c.label();

  // goto(1) replaces the session type with the type at index 1
  let _: Chan<Cons<Close, Nil>, Close> = c.goto();
}

How does Tyrade work?

Consider the translation of TAdd. Here's the Tyrade definition:

fn TAdd<N1, N2>() {
  match N1 {
    Z => N2,
    S(N3) => TAdd(N3, S(N2))
  }
}

And here's the generated Rust code:

pub trait ComputeTAdd<N2> {
    type Output;
}

pub type TAdd<N1, N2> = <N1 as ComputeTAdd<N2>>::Output;

impl<N2> ComputeTAdd<N2> for Z {
    type Output = N2;
}

impl<N3, N2> ComputeTAdd<N2> for S<N3>
where
    N3: ComputeTAdd<S<N2>>
{
    type Output = TAdd<N3, S<N2>>;
}

At a high level, Tyrade does the following for you:

  1. The compiler sets up the necessary traits and type definitions (ComputeTAdd and TAdd).
  2. While compiling the operators to types, all operations are added as where constraints. For example, TAdd(N3, S(N2)) creates the constraint N3: ComputeTAdd<S<N2>>.
  3. The compiler generates a different impl for each match branch. In the case of multiple matches, e.g. as in MaxLevel, the compiler generates an impl for the cartesian product of all match branches.

See trans.rs for the details.

Next steps

Tyrade is experimental, meaning I'm still discovering the boundaries of what's possible. There are two main areas of inquiry:

  1. What type-language mechanisms does Rust's trait system permit? For example, I was not able to implement == since type equality in Rust doesn't quite work as we need it. Higher-kinded types would be useful as well to enable proper polymorphic type functions.

  2. What application areas can benefit from a type-level programming language? Session types are the most complex example I've seen so far, but I'd be really interested to find other use cases for Tyrade.

Please let me know if you'd be interested in using or contributing to Tyrade! Email me at [email protected].

More Repositories

1

flowistry

Flowistry is an IDE plugin for Rust that helps you focus on relevant code.
Rust
1,812
star
2

lia

A high-level language for Rust
Rust
305
star
3

terracuda

A high-level Lua API for GPU parallelism [15-418 final]
Perl
64
star
4

indexical

Human-friendly indexed collections
Rust
45
star
5

sevenwonders

The popular Seven Wonders board game on the web
PHP
40
star
6

corrset-benchmark

A repository to test different performance optimizations in a sparse matrix computation.
Jupyter Notebook
37
star
7

rabbot

Abstract binding tree code generator
Rust
35
star
8

inliner

Programmable, human-readable inlining of Python code
Python
29
star
9

rustc-type-metaprogramming

Rust
24
star
10

cmu-grades

Gets your grades from CMU services
Python
21
star
11

wordtree

A Python library for generating word tree diagrams
Python
20
star
12

learn-opengl-rust

Learn OpenGL in Rust with web compatibility
Rust
16
star
13

model-js-workspace

My personal standard for how to set up a Javascript workspace
TypeScript
13
star
14

rust-book-exercises

Rust
12
star
15

types-over-strings

Rust
12
star
16

pyro-under-the-hood

Jupyter Notebook
8
star
17

cargo-single-pyo3

Generate a Python module from a single Rust file.
Rust
8
star
18

hyperlapse

Open-source implementation of MSR Hyperlapse
C++
8
star
19

gcp-job-queue

Python
7
star
20

r-autota

A tool to make R error messages easier to understand
HTML
6
star
21

rust-editor

Rust
6
star
22

web-logger

A prototype Rust logger that uses the browser instead of the console.
Rust
5
star
23

411-rust-starter-code

Rust
5
star
24

example-analyzer

Rust
5
star
25

rust-api-type-patterns

5
star
26

algo-rs

Assorted algorithms implemented in Rust
Rust
4
star
27

simple-bind

One-line non-exhaustive binds in Rust
Rust
4
star
28

aoc2021

q
4
star
29

dotfiles

My emacs configuration
Emacs Lisp
4
star
30

cmu_auth

Simple Python methods for authenticating to CMU services
Python
4
star
31

so-lang-diversity-analysis

Analysis of diversity in language communities based on the Stack Overflow Developer Survey 2022
Jupyter Notebook
3
star
32

expressiveness-benchmark

Jupyter Notebook
3
star
33

dml

Derma Markup Language, for HTML-style UI creation (for Garry's Mod)
Lua
3
star
34

classity

Remote lecturing tool [hackathon project]
CSS
3
star
35

y0

A language?
OCaml
3
star
36

tquery

jQuery, but for types
Rust
2
star
37

psypl-experiments

Jupyter Notebook
2
star
38

nota

JavaScript
2
star
39

gentle_whisper

Automatic transcription + fine-grained time-alignment
Python
2
star
40

tacticalassault

Class-based FPS gamemode (for Garry's Mod)
Lua
2
star
41

mdbook-preprocessor-utils

Rust
2
star
42

pickle-cache

Small utility for easily and efficiently saving/loading Python values to disk.
Python
2
star
43

utilikilt

CMU services on your phone
Objective-C
2
star
44

autoplan

Jupyter Notebook
2
star
45

glen

Three.js extension for creating games in the browser.
JavaScript
2
star
46

willcrichton.github.io

Github site
HTML
2
star
47

react-sequence-typed

TypeScript
2
star
48

rustc-embed

Rust
1
star
49

walrus-locator

In search of places to put a walrus
1
star
50

crashcourse-chat

For CrashCourse at CMU
JavaScript
1
star
51

bevy_world_visualizer

Rust
1
star
52

lagooned

A game of exploration and mystery
JavaScript
1
star
53

tetedoro

JavaScript
1
star
54

context-var

React-style context variables (i.e. dynamic scoping)
Python
1
star
55

semantic-divergence

Rust
1
star
56

fishies

A Paper.js game about fish. [98-232 sample project]
JavaScript
1
star
57

rlu-rs

A Rust implementation of the Read-Log-Update concurrency mechanism
Rust
1
star
58

notifier

Python
1
star
59

lecture-intelligence

A tool for scraping and analyzing lecture video viewing data from Panopto.
Jupyter Notebook
1
star
60

hudd

HUD Designer - intuitive menu creator for Garry's Mod
Lua
1
star
61

awap-2015

Official repository for Algorithms with a Purpose 2015: Startup Tycoon
Python
1
star
62

regulair

Regex library for FPGAs
Python
1
star
63

status-protect

Chrome extension designed to folly malicious Facebook status-updaters
JavaScript
1
star