{"id":17166549,"url":"https://github.com/willcrichton/tyrade","last_synced_at":"2025-10-07T22:49:17.223Z","repository":{"id":46834195,"uuid":"272497640","full_name":"willcrichton/tyrade","owner":"willcrichton","description":"A pure functional language for type-level programming in Rust","archived":false,"fork":false,"pushed_at":"2022-11-12T23:05:19.000Z","size":81,"stargazers_count":327,"open_issues_count":4,"forks_count":13,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-05-19T20:11:25.724Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/willcrichton.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-06-15T17:05:55.000Z","updated_at":"2025-05-01T03:37:29.000Z","dependencies_parsed_at":"2022-08-20T17:51:13.997Z","dependency_job_id":null,"html_url":"https://github.com/willcrichton/tyrade","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/willcrichton/tyrade","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willcrichton%2Ftyrade","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willcrichton%2Ftyrade/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willcrichton%2Ftyrade/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willcrichton%2Ftyrade/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/willcrichton","download_url":"https://codeload.github.com/willcrichton/tyrade/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willcrichton%2Ftyrade/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265846885,"owners_count":23838160,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T23:06:00.138Z","updated_at":"2025-10-07T22:49:12.174Z","avatar_url":"https://github.com/willcrichton.png","language":"Rust","funding_links":[],"categories":["Functional"],"sub_categories":[],"readme":"# Tyrade: a pure functional language for type-level programming in Rust\n\n![ci](https://github.com/willcrichton/tyrade/workflows/ci/badge.svg)\n\n* [Motivating example: security types](#motivating-example-security-types)\n* [More complex example: session and list types](#more-complex-example-session-and-list-types)\n* [How does Tyrade work?](#how-does-tyrade-work)\n* [Next steps](#next-steps)\n\nTyrade 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:\n\n```rust\ntyrade! {\n  // A type-level enum for Peano numerals, with \"Z\" (zero) and \"S(n)\" (successor).\n  enum TNum {\n    Z,\n    S(TNum)\n  }\n\n  // A function that adds two Peano numerals together.\n  fn TAdd\u003cN1, N2\u003e() {\n    match N1 {\n      Z =\u003e N2,\n      S(N3) =\u003e TAdd(N3, S(N2))\n    }\n  }\n}\n\nfn num_tests() {\n  // 1 + 1 == 2\n  assert_type_eq::\u003cS\u003cS\u003cZ\u003e\u003e, TAdd\u003cS\u003cZ\u003e, S\u003cZ\u003e\u003e\u003e();\n}\n```\n\nAt 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: \u003chttps://willcrichton.net/notes/type-level-programming/\u003e\n\n## Motivating example: security types\n\nOthers have shown that Rust traits are [Turing-complete](https://sdleffler.github.io/RustTypeSystemTuringComplete/) and can be used for e.g. [Fizz-Buzz](https://github.com/doctorn/trait-eval). 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.\n\nAs a simple example, consider two types `HighSec` and `LowSec` representing the security of an item:\n\n```rust\nstruct High;\nstruct Low;\n\nstruct Item\u003cT, Sec\u003e {\n  t: T,\n  _sec: PhantomData\u003cSec\u003e\n}\n```\n\nA 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.\n\n```rust\ntrait ComputeMaxLevel\u003cOther\u003e {\n  type Output;\n}\n\n// These impls define the core computation\nimpl ComputeMaxLevel\u003cLow\u003e  for Low  { type Output = Low;  }\nimpl ComputeMaxLevel\u003cHigh\u003e for Low  { type Output = High; }\nimpl ComputeMaxLevel\u003cLow\u003e  for High { type Output = High; }\nimpl ComputeMaxLevel\u003cHigh\u003e for High { type Output = High; }\n\n// The type alias gives us a more convenient way to \"call\" the type operator\ntype MaxLevel\u003cL, R\u003e = \u003cL as ComputeMaxLevel\u003cR\u003e\u003e::Output;\n\nfn sec_tests() {\n  // example unit tests\n  assert_type_eq::\u003cLow,  MaxLevel\u003cLow, Low\u003e\u003e();\n  assert_type_eq::\u003cHigh, MaxLevel\u003cLow, High\u003e\u003e();\n}\n```\n\nThe goal of Tyrade is to perform this translation automatically from a functional programming model. Using Tyrade, this program is written as:\n\n```rust\ntyrade!{\n  enum Security {\n    Low,\n    High\n  }\n\n  fn MaxLevel\u003cS1, S2\u003e() {\n    match S1 {\n      Low =\u003e match S2 {\n        Low =\u003e Low,\n        High =\u003e High\n      }\n      High =\u003e High\n    }\n  }\n\n  // In the high-level language, we can more easily see a chance for simplification.\n  fn MaxLevel2\u003cS1, S2\u003e() {\n    match S1 {\n      Low =\u003e S2,\n      High =\u003e High\n    }\n  }\n}\n```\n\nThis way, both the type definition and the type-level program are expressed using familiar constructs like `fn`, `enum`, and `match`.\n\n## More complex example: session and list types\n\nTyrade can be used to define a framework for communication protocols, e.g. [session types](https://github.com/Munksgaard/session-types/). For example, the session types and their duals can be defined as follows:\n\n```rust\ntyrade! {\n  enum SessionType {\n    Close,\n    Recv(Type, SessionType),\n    Send(Type, SessionType),\n    Choose(SessionType, SessionType),\n    Offer(SessionType, SessionType),\n    Label(SessionType),\n    Goto(TNum)\n  }\n\n  fn Dual\u003cS\u003e() {\n    match S {\n      Close =\u003e S,\n      Recv(T, S2) =\u003e Send(T, Dual(S2)),\n      Send(T, S2) =\u003e Recv(T, Dual(S2)),\n      Choose(S2, S3) =\u003e Offer(Dual(S2), Dual(S3)),\n      Offer(S2, S3) =\u003e Choose(Dual(S2), Dual(S3)),\n      Label(S2) =\u003e Label(Dual(S2)),\n      Goto(N) =\u003e S\n    }\n  }\n}\n\nfn session_type_test() {\n  // The dual of a Send is a Recv\n  assert_type_eq::\u003c\n    Dual\u003cSend\u003ci32, Close\u003e\u003e,\n    Recv\u003ci32, Close\u003e    \n  \u003e();\n}\n```\n\nTyrade provides a standard library of type-level building blocks like [booleans](https://github.com/willcrichton/tyrade/blob/master/src/tbool.rs), [numbers](https://github.com/willcrichton/tyrade/blob/master/src/tnum.rs), and [lists](https://github.com/willcrichton/tyrade/blob/master/src/tlist.rs). For example, we can use lists to implement the compile-time saving and indexing of jump points in session types.\n\n```rust\nstruct Chan\u003cEnv, S\u003e(PhantomData\u003c(Env, S)\u003e);\n\nimpl\u003cEnv: TList, S: SessionType\u003e Chan\u003cEnv, Label\u003cS\u003e\u003e {\n  // label() pushes a type S onto the environment\n  fn label(self) -\u003e Chan\u003cCons\u003cS, Env\u003e, S\u003e {\n    Chan(PhantomData)\n  }\n}\n\nimpl\u003cEnv: TList, N: TNum\u003e Chan\u003cEnv, Goto\u003cN\u003e\u003e\nwhere Env: ComputeTListNth\u003cN\u003e + ComputeTListSkip\u003cN\u003e\n{\n  // goto\u003cN\u003e gets the Nth type from the environment, removing every type\n  // before then\n  fn goto(self) -\u003e Chan\u003cTListSkip\u003cEnv, N\u003e, TListNth\u003cEnv, N\u003e\u003e {\n    Chan(PhantomData)\n  }\n}\n\n\nfn session_type_test() {\n  let c: Chan\u003c\n      Cons\u003cClose, Nil\u003e,\n      Label\u003cGoto\u003cS\u003cZ\u003e\u003e\u003e\u003e = Chan(PhantomData);\n\n  // label() pushes Goto onto the Env list\n  let c: Chan\u003c\n      Cons\u003cGoto\u003cS\u003cZ\u003e\u003e, Cons\u003cClose, Nil\u003e\u003e,\n      Goto\u003cS\u003cZ\u003e\u003e\u003e = c.label();\n\n  // goto(1) replaces the session type with the type at index 1\n  let _: Chan\u003cCons\u003cClose, Nil\u003e, Close\u003e = c.goto();\n}\n```\n\n## How does Tyrade work?\n\nConsider the translation of `TAdd`. Here's the Tyrade definition:\n\n```rust\nfn TAdd\u003cN1, N2\u003e() {\n  match N1 {\n    Z =\u003e N2,\n    S(N3) =\u003e TAdd(N3, S(N2))\n  }\n}\n```\n\nAnd here's the generated Rust code:\n\n```rust\npub trait ComputeTAdd\u003cN2\u003e {\n    type Output;\n}\n\npub type TAdd\u003cN1, N2\u003e = \u003cN1 as ComputeTAdd\u003cN2\u003e\u003e::Output;\n\nimpl\u003cN2\u003e ComputeTAdd\u003cN2\u003e for Z {\n    type Output = N2;\n}\n\nimpl\u003cN3, N2\u003e ComputeTAdd\u003cN2\u003e for S\u003cN3\u003e\nwhere\n    N3: ComputeTAdd\u003cS\u003cN2\u003e\u003e\n{\n    type Output = TAdd\u003cN3, S\u003cN2\u003e\u003e;\n}\n```\n\nAt a high level, Tyrade does the following for you:\n1. The compiler sets up the necessary traits and type definitions (`ComputeTAdd` and `TAdd`).\n2. While compiling the operators to types, all operations are added as `where` constraints. For example, `TAdd(N3, S(N2))` creates the constraint `N3: ComputeTAdd\u003cS\u003cN2\u003e\u003e`.\n3. 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.\n\nSee [trans.rs](https://github.com/willcrichton/tyrade/blob/master/tyrade-macro/src/trans.rs) for the details.\n\n## Next steps\n\nTyrade is experimental, meaning I'm still discovering the boundaries of what's possible. There are two main areas of inquiry:\n\n1. 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.\n\n2. 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.\n\nPlease let me know if you'd be interested in using or contributing to Tyrade! Email me at wcrichto@cs.stanford.edu.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillcrichton%2Ftyrade","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwillcrichton%2Ftyrade","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillcrichton%2Ftyrade/lists"}