{"id":13749187,"url":"https://github.com/brendanzab/moniker","last_synced_at":"2025-03-17T00:32:26.617Z","repository":{"id":57640078,"uuid":"125940101","full_name":"brendanzab/moniker","owner":"brendanzab","description":"Automagical variable binding library for Rust","archived":false,"fork":false,"pushed_at":"2019-10-04T21:59:43.000Z","size":319,"stargazers_count":67,"open_issues_count":10,"forks_count":6,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-05-22T21:34:34.534Z","etag":null,"topics":["abstract-binding-trees","alpha-equivalence","debruijn-indices","derive","locally-nameless","programming-languages","rust","unbound","variable-binding"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brendanzab.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-03-20T00:56:23.000Z","updated_at":"2024-05-07T15:45:57.000Z","dependencies_parsed_at":"2022-09-07T16:03:29.064Z","dependency_job_id":null,"html_url":"https://github.com/brendanzab/moniker","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brendanzab%2Fmoniker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brendanzab%2Fmoniker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brendanzab%2Fmoniker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brendanzab%2Fmoniker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brendanzab","download_url":"https://codeload.github.com/brendanzab/moniker/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221669339,"owners_count":16860855,"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":["abstract-binding-trees","alpha-equivalence","debruijn-indices","derive","locally-nameless","programming-languages","rust","unbound","variable-binding"],"created_at":"2024-08-03T07:00:56.656Z","updated_at":"2024-10-27T11:35:26.609Z","avatar_url":"https://github.com/brendanzab.png","language":"Rust","funding_links":[],"categories":["Projects"],"sub_categories":["Libraries"],"readme":"# Moniker\n\n[![Build Status][travis-badge]][travis-url]\n[![Crates.io][crate-badge]][crate-url]\n[![Docs.rs][docs-badge]][docs-url]\n[![Gitter][gitter-badge]][gitter-lobby]\n\n[travis-badge]: https://travis-ci.org/brendanzab/moniker.svg?branch=master\n[travis-url]: https://travis-ci.org/brendanzab/moniker\n[crate-url]: https://crates.io/crates/moniker\n[crate-badge]: https://img.shields.io/crates/v/moniker.svg\n[docs-url]: https://docs.rs/moniker\n[docs-badge]: https://docs.rs/moniker/badge.svg\n[gitter-badge]: https://badges.gitter.im/brendanzab/moniker.svg\n[gitter-lobby]: https://gitter.im/brendanzab/moniker\n\nMoniker makes it simple to track variables across nested scopes in\nprogramming language implementations.\n\nInstead of implementing name-handling code by hand (which is often tedious and\nerror-prone), Moniker provides a number of types and traits for declaratively\ndescribing name binding directly in your language's abstract syntax tree. From\nthis we can derive the corresponding name-handling code automatically!\n\n## Motivation\n\nIt's interesting to note that the idea of a 'scope' comes up quite often in\nprogramming:\n\n| Description          | Rust Example                                 |\n| -------------------- | -------------------------------------------- |\n| case match arms      | ``match expr { x =\u003e /* `x` bound here */ }`` |\n| let bindings         | ``let x = ...; /* `x` bound here */``        |\n| recursive functions  | ``fn foo() { /* `foo` bound here */ }``      |\n| functions parameters | ``fn foo(x: T) { /* `x` bound here */ }``    |\n| recursive types      | ``enum List { Nil, /* `List` bound here */`` |\n| type parameters      | ``struct Point\u003cT\u003e { /* `T` bound here */ }`` |\n\nFor example, let's take a silly example of a Rust function:\n\n```rust\ntype Count = u32;\n\nfn silly\u003cT\u003e((count, data): (Count, T)) -\u003e T {\n    match count {\n        0 =\u003e data,\n        count =\u003e silly((count - 1, data)),\n    }\n}\n```\n\nThere's actually lots of name-binding at play here! Let's connect the binders\nto their corresponding binders:\n\n```rust\n//            ?\n//            |\n//            v\ntype Count = u32;\n//     |\n//     '----------------------.\n//  .-------------------------|------------------.\n//  |    .--------------------|----*------.      |\n//  |    |                    |    |      |      |\n//  |    |                    v    v      v      |\nfn silly\u003cT\u003e((count, data): (Count, T)) -\u003e T { // |\n//             |      |                          |\n//          .--'      |                          |\n//          |         *--------------.           |\n//          v         |              |           |\n    match count { //  |              |           |\n//             .------'              |           |\n//             |                     |           |\n//             v                     |           |\n        0 =\u003e data, //                |           |\n//         .--------------.          |           |\n//         |              |          |           |\n//         |              v          v           |\n        count =\u003e silly((count - 1, data)), //    |\n//                 ^                             |\n//                 |                             |\n//                 '-----------------------------'\n    }\n}\n```\n\nKeeping track of the relationships between these variables can be a pain, and\ncan become especially error-prone when implementing evaluators and type\ncheckers. Moniker aims to support all of these binding structures, with\nminimal pain!\n\n## Example\n\nHere is how we would use Moniker to describe a very small functional language,\nwith variables, anonymous functions, applications, and let bindings:\n\n```rust\n#[macro_use]\nextern crate moniker;\n\nuse moniker::{Embed, Binder, Rec, Scope, Var};\nuse std::rc::Rc;\n\n/// Expressions\n///\n/// ```text\n/// e ::= x               variables\n///     | \\x =\u003e e         anonymous functions\n///     | e₁ e₂           function application\n/// ````\n#[derive(Debug, Clone, BoundTerm)]\n//                        ^\n//                        |\n//              The derived `BoundTerm` implementation\n//              does all of the heavy-lifting!\npub enum Expr {\n    /// Variables\n    Var(Var\u003cString\u003e),\n    /// Anonymous functions (ie. lambda expressions)\n    Lam(Scope\u003cBinder\u003cString\u003e, RcExpr\u003e),\n    /// Function applications\n    App(RcExpr, RcExpr),\n}\n\npub type RcExpr = Rc\u003cExpr\u003e;\n```\n\nWe can now construct lambda expressions by doing the following:\n\n```rust\nlet f = FreeVar::fresh(Some(\"f\".to_string()));\nlet x = FreeVar::fresh(Some(\"x\".to_string()));\n\n// \\f =\u003e \\x =\u003e f x\nRc::new(Expr::Lam(Scope::new(\n    Binder(f.clone()),\n    Rc::new(Expr::Lam(Scope::new(\n        Binder(x.clone()),\n        Rc::new(Expr::App(\n            Rc::new(Expr::Var(Var::Free(f.clone()))),\n            Rc::new(Expr::Var(Var::Free(x.clone()))),\n        )),\n    )))\n)))\n```\n\n### More Complete Examples\n\nMore complete examples, including evaluators and type checkers, can be found\nunder the [`moniker/examples`](/moniker/examples) directory.\n\n| Example Name          | Description                 |\n| --------------------- | --------------------------- |\n| [`lc`]                | untyped lambda calculus |\n| [`lc_let`]            | untyped lambda calculus with nested let bindings |\n| [`lc_letrec`]         | untyped lambda calculus with mutually recursive bindings |\n| [`lc_multi`]          | untyped lambda calculus with multi-binders |\n| [`stlc`]              | simply typed lambda calculus with literals |\n| [`stlc_data`]         | simply typed lambda calculus with records, variants, literals, and pattern matching |\n| [`stlc_data_isorec`]  | simply typed lambda calculus with records, variants, literals, pattern matching, and iso-recursive types |\n\n[`lc`]: /moniker/examples/lc.rs\n[`lc_let`]: /moniker/examples/lc_let.rs\n[`lc_letrec`]: /moniker/examples/lc_letrec.rs\n[`lc_multi`]: /moniker/examples/lc_multi.rs\n[`stlc`]: /moniker/examples/stlc.rs\n[`stlc_data`]: /moniker/examples/stlc_data.rs\n[`stlc_data_isorec`]: /moniker/examples/stlc_data_isorec.rs\n\n### Projects using Moniker\n\nMoniker is currently used in the following Rust projects:\n\n- [Pikelet](https://github.com/pikelet-lang/pikelet): A dependently typed\n  systems programming language\n\n## Overview of traits and data types\n\nWe separate data types into terms and patterns:\n\n### Terms\n\nTerms are data types that implement the [`BoundTerm`] trait.\n\n- [`Var\u003cN\u003e`]: A variable that is either free or bound\n- [`Scope\u003cP: BoundPattern\u003cN\u003e, T: BoundTerm\u003cN\u003e\u003e`]: bind the term `T` using the pattern `P`\n- [`Ignore\u003cT\u003e`]: Ignores `T` when comparing for alpha equality\n\nImplementations for tuples, strings, numbers, slices, vectors, and smart pointers\nare also provided for convenience.\n\n[`BoundTerm`]: https://docs.rs/moniker/latest/moniker/trait.BoundTerm.html\n[`Var\u003cN\u003e`]: https://docs.rs/moniker/latest/moniker/enum.Var.html\n[`Scope\u003cP: BoundPattern\u003cN\u003e, T: BoundTerm\u003cN\u003e\u003e`]: https://docs.rs/moniker/latest/moniker/struct.Scope.html\n[`Ignore\u003cT\u003e`]: https://docs.rs/moniker/latest/moniker/struct.Ignore.html\n\n### Patterns\n\nPatterns are data types that implement the [`BoundPattern`] trait.\n\n- [`Binder\u003cN\u003e`]: Captures a free variables within a term, but is ignored for alpha equality\n- [`Ignore\u003cT\u003e`]: Ignores `T` when comparing for alpha equality\n- [`Embed\u003cT: BoundTerm\u003cN\u003e\u003e`]: Embed a term `T` in a pattern\n- [`Nest\u003cP: BoundPattern\u003cN\u003e\u003e`]: Multiple nested binding patterns\n- [`Rec\u003cP: BoundPattern\u003cN\u003e\u003e`]: Recursively bind a pattern in itself\n\nImplementations for tuples, strings, numbers, slices, vectors, and smart pointers\nare also provided for convenience.\n\n[`BoundPattern`]: https://docs.rs/moniker/latest/moniker/trait.BoundPattern.html\n[`Binder\u003cN\u003e`]: https://docs.rs/moniker/latest/moniker/enum.Binder.html\n[`Embed\u003cT: BoundTerm\u003cN\u003e\u003e`]: https://docs.rs/moniker/latest/moniker/struct.Embed.html\n[`Nest\u003cP: BoundPattern\u003cN\u003e\u003e`]: https://docs.rs/moniker/latest/moniker/struct.Nest.html\n[`Rec\u003cP: BoundPattern\u003cN\u003e\u003e`]: https://docs.rs/moniker/latest/moniker/struct.Rec.html\n\n## Roadmap\n\nMoniker is currently good enough to use for initial language prototypes, but\nthere is more work that we'd like to do to make it a more fully-featured\nname binding and scope handling toolkit.\n\n- [ ] Initial implementation using a locally nameless representation\n    - [x] Implement basic type combinators\n        - [x] `Embed`\n        - [x] `Ignore`\n        - [x] `Nest`\n        - [x] `Rec`\n        - [x] `Scope`\n    - [ ] Automatically derive traits\n        - [x] `BoundTerm`\n        - [x] `BoundPattern`\n        - [ ] `Subst`\n    - [ ] Allow derives to use identifier types other than `String`\n    - [ ] Implement namespaced variables and binders\n    - [ ] Performance optimizations\n        - [ ] Cache max-depth of terms\n        - [ ] Cache free variables of terms\n        - [ ] Perform multiple-opening/closing\n        - [ ] Use visitors\n- [ ] Explore implementing other name-binding schemes\n    - [ ] Named with unique indices\n    - [ ] Scope Graphs\n    - [ ] ...?\n\n## References\n\nHere is a list of interesting references and prior art that was helpful when\nbuilding Moniker. Note that it isn't necessary to read and understand these to\nuse the library, but they _might_ be useful if you would like to contribute!\n\n### Papers\n\n- [The Locally Nameless Representation](https://www.chargueraud.org/research/2009/ln/main.pdf)\n- [Binders Unbound](http://www.seas.upenn.edu/~sweirich/papers/icfp11.pdf)\n- [An overview of Cαml](http://pauillac.inria.fr/~fpottier/publis/fpottier-alphacaml.pdf)\n- [Visitors Unchained](http://gallium.inria.fr/~fpottier/publis/fpottier-visitors-unchained.pdf)\n- [Engineering Formal Metatheory](http://www.chargueraud.org/research/2007/binders/binders_popl_08.pdf)\n\n### Blog Posts\n\n- [How I learned to stop worrying and love de Bruijn indices](http://disciple-devel.blogspot.com.au/2011/08/how-i-learned-to-stop-worrying-and-love.html)\n- [Announcing DBLib](http://gallium.inria.fr/blog/announcing-dblib/)\n- [Bound](https://www.schoolofhaskell.com/user/edwardk/bound)\n\n### Other Libraries\n\nThe API was mainly inspired by the [Unbound][unbound] and\n[Unbound-Generics][unbound-generics] libraries for Haskell, with some\ndifferences. The main change that we make is to have two separate traits\n(`BoundTerm` and `BoundPattern`) in place of Unbound's single `Alpha` type\nclass. We've found that this better captures the semantics of the library, and\ngreatly cuts down on the potential for accidental misuse.\n\nOther auto-binding libraries exist for a number of different languages:\n\n- [Unbound][unbound]: Specify the binding structure of your data type with an\n  expressive set of type combinators, and Unbound handles the rest!\n  Automatically derives alpha-equivalence, free variable calculation,\n  capture-avoiding substitution, and more.\n- [Unbound-Generics][unbound-generics]: an independent re-implementation of\n  Unbound but using GHC.Generics instead of RepLib.\n- [Cαml][alphaCaml]: a tool that turns a so-called \"binding specification\" into\n  an OCaml compilation unit.\n- [alphaLib][alphaLib]: An OCaml library that helps deal with binding constructs\n  in abstract syntax trees.\n- [abbot][abbot]: Generation of abstract binding trees for SML\n- [rabbot][rabbot]: A port of SML's Abbot to Rust\n- [DBLib][dblib]: Facilities for working with de Bruijn indices in Coq\n- [Bound](https://github.com/ekmett/bound/): DeBruijn indices for Haskell\n- [Metalib][metalib]: The Penn Locally Nameless Metatheory Library\n- [LibLN][ln]: Locally nameless representation with cofinite quantification\n\n[unbound]: https://github.com/sweirich/replib\n[unbound-generics]: https://github.com/lambdageek/unbound-generics\n[alphaCaml]: http://pauillac.inria.fr/~fpottier/alphaCaml/alphacaml.html.en\n[alphaLib]: https://gitlab.inria.fr/fpottier/alphaLib\n[abbot]: https://github.com/robsimmons/abbot\n[rabbot]: https://github.com/willcrichton/rabbot\n[dblib]: https://github.com/coq-contribs/dblib\n[metalib]: https://github.com/plclub/metalib\n[ln]: http://www.chargueraud.org/softs/ln/\n\n## Contributing\n\nWe really want to encourage new contributors to help out! Please come chat with\nus [on our Gitter channel][gitter-lobby] - if you have any questions about the\nproject, or just want to say hi!\n\n## Acknowledgments\n\n[![YesLogic Logo][yeslogic-logo]][yeslogic]\n\nThis work was done in part with the generous support of [YesLogic][yeslogic].\n\n[yeslogic]: http://yeslogic.com/\n[yeslogic-logo]: assets/yeslogic-logo.png\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrendanzab%2Fmoniker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrendanzab%2Fmoniker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrendanzab%2Fmoniker/lists"}