Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/brendanzab/moniker
Automagical variable binding library for Rust
https://github.com/brendanzab/moniker
abstract-binding-trees alpha-equivalence debruijn-indices derive locally-nameless programming-languages rust unbound variable-binding
Last synced: 10 days ago
JSON representation
Automagical variable binding library for Rust
- Host: GitHub
- URL: https://github.com/brendanzab/moniker
- Owner: brendanzab
- License: apache-2.0
- Created: 2018-03-20T00:56:23.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2019-10-04T21:59:43.000Z (about 5 years ago)
- Last Synced: 2024-05-22T21:34:34.534Z (6 months ago)
- Topics: abstract-binding-trees, alpha-equivalence, debruijn-indices, derive, locally-nameless, programming-languages, rust, unbound, variable-binding
- Language: Rust
- Homepage:
- Size: 312 KB
- Stars: 67
- Watchers: 9
- Forks: 6
- Open Issues: 10
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
- awesome-rust-formalized-reasoning - moniker - derive):star::zzz: - automagical variable binding library. (Projects / Libraries)
README
# Moniker
[![Build Status][travis-badge]][travis-url]
[![Crates.io][crate-badge]][crate-url]
[![Docs.rs][docs-badge]][docs-url]
[![Gitter][gitter-badge]][gitter-lobby][travis-badge]: https://travis-ci.org/brendanzab/moniker.svg?branch=master
[travis-url]: https://travis-ci.org/brendanzab/moniker
[crate-url]: https://crates.io/crates/moniker
[crate-badge]: https://img.shields.io/crates/v/moniker.svg
[docs-url]: https://docs.rs/moniker
[docs-badge]: https://docs.rs/moniker/badge.svg
[gitter-badge]: https://badges.gitter.im/brendanzab/moniker.svg
[gitter-lobby]: https://gitter.im/brendanzab/monikerMoniker makes it simple to track variables across nested scopes in
programming language implementations.Instead of implementing name-handling code by hand (which is often tedious and
error-prone), Moniker provides a number of types and traits for declaratively
describing name binding directly in your language's abstract syntax tree. From
this we can derive the corresponding name-handling code automatically!## Motivation
It's interesting to note that the idea of a 'scope' comes up quite often in
programming:| Description | Rust Example |
| -------------------- | -------------------------------------------- |
| case match arms | ``match expr { x => /* `x` bound here */ }`` |
| let bindings | ``let x = ...; /* `x` bound here */`` |
| recursive functions | ``fn foo() { /* `foo` bound here */ }`` |
| functions parameters | ``fn foo(x: T) { /* `x` bound here */ }`` |
| recursive types | ``enum List { Nil, /* `List` bound here */`` |
| type parameters | ``struct Point { /* `T` bound here */ }`` |For example, let's take a silly example of a Rust function:
```rust
type Count = u32;fn silly((count, data): (Count, T)) -> T {
match count {
0 => data,
count => silly((count - 1, data)),
}
}
```There's actually lots of name-binding at play here! Let's connect the binders
to their corresponding binders:```rust
// ?
// |
// v
type Count = u32;
// |
// '----------------------.
// .-------------------------|------------------.
// | .--------------------|----*------. |
// | | | | | |
// | | v v v |
fn silly((count, data): (Count, T)) -> T { // |
// | | |
// .--' | |
// | *--------------. |
// v | | |
match count { // | | |
// .------' | |
// | | |
// v | |
0 => data, // | |
// .--------------. | |
// | | | |
// | v v |
count => silly((count - 1, data)), // |
// ^ |
// | |
// '-----------------------------'
}
}
```Keeping track of the relationships between these variables can be a pain, and
can become especially error-prone when implementing evaluators and type
checkers. Moniker aims to support all of these binding structures, with
minimal pain!## Example
Here is how we would use Moniker to describe a very small functional language,
with variables, anonymous functions, applications, and let bindings:```rust
#[macro_use]
extern crate moniker;use moniker::{Embed, Binder, Rec, Scope, Var};
use std::rc::Rc;/// Expressions
///
/// ```text
/// e ::= x variables
/// | \x => e anonymous functions
/// | e₁ e₂ function application
/// ````
#[derive(Debug, Clone, BoundTerm)]
// ^
// |
// The derived `BoundTerm` implementation
// does all of the heavy-lifting!
pub enum Expr {
/// Variables
Var(Var),
/// Anonymous functions (ie. lambda expressions)
Lam(Scope, RcExpr>),
/// Function applications
App(RcExpr, RcExpr),
}pub type RcExpr = Rc;
```We can now construct lambda expressions by doing the following:
```rust
let f = FreeVar::fresh(Some("f".to_string()));
let x = FreeVar::fresh(Some("x".to_string()));// \f => \x => f x
Rc::new(Expr::Lam(Scope::new(
Binder(f.clone()),
Rc::new(Expr::Lam(Scope::new(
Binder(x.clone()),
Rc::new(Expr::App(
Rc::new(Expr::Var(Var::Free(f.clone()))),
Rc::new(Expr::Var(Var::Free(x.clone()))),
)),
)))
)))
```### More Complete Examples
More complete examples, including evaluators and type checkers, can be found
under the [`moniker/examples`](/moniker/examples) directory.| Example Name | Description |
| --------------------- | --------------------------- |
| [`lc`] | untyped lambda calculus |
| [`lc_let`] | untyped lambda calculus with nested let bindings |
| [`lc_letrec`] | untyped lambda calculus with mutually recursive bindings |
| [`lc_multi`] | untyped lambda calculus with multi-binders |
| [`stlc`] | simply typed lambda calculus with literals |
| [`stlc_data`] | simply typed lambda calculus with records, variants, literals, and pattern matching |
| [`stlc_data_isorec`] | simply typed lambda calculus with records, variants, literals, pattern matching, and iso-recursive types |[`lc`]: /moniker/examples/lc.rs
[`lc_let`]: /moniker/examples/lc_let.rs
[`lc_letrec`]: /moniker/examples/lc_letrec.rs
[`lc_multi`]: /moniker/examples/lc_multi.rs
[`stlc`]: /moniker/examples/stlc.rs
[`stlc_data`]: /moniker/examples/stlc_data.rs
[`stlc_data_isorec`]: /moniker/examples/stlc_data_isorec.rs### Projects using Moniker
Moniker is currently used in the following Rust projects:
- [Pikelet](https://github.com/pikelet-lang/pikelet): A dependently typed
systems programming language## Overview of traits and data types
We separate data types into terms and patterns:
### Terms
Terms are data types that implement the [`BoundTerm`] trait.
- [`Var`]: A variable that is either free or bound
- [`Scope, T: BoundTerm>`]: bind the term `T` using the pattern `P`
- [`Ignore`]: Ignores `T` when comparing for alpha equalityImplementations for tuples, strings, numbers, slices, vectors, and smart pointers
are also provided for convenience.[`BoundTerm`]: https://docs.rs/moniker/latest/moniker/trait.BoundTerm.html
[`Var`]: https://docs.rs/moniker/latest/moniker/enum.Var.html
[`Scope, T: BoundTerm>`]: https://docs.rs/moniker/latest/moniker/struct.Scope.html
[`Ignore`]: https://docs.rs/moniker/latest/moniker/struct.Ignore.html### Patterns
Patterns are data types that implement the [`BoundPattern`] trait.
- [`Binder`]: Captures a free variables within a term, but is ignored for alpha equality
- [`Ignore`]: Ignores `T` when comparing for alpha equality
- [`Embed>`]: Embed a term `T` in a pattern
- [`Nest>`]: Multiple nested binding patterns
- [`Rec>`]: Recursively bind a pattern in itselfImplementations for tuples, strings, numbers, slices, vectors, and smart pointers
are also provided for convenience.[`BoundPattern`]: https://docs.rs/moniker/latest/moniker/trait.BoundPattern.html
[`Binder`]: https://docs.rs/moniker/latest/moniker/enum.Binder.html
[`Embed>`]: https://docs.rs/moniker/latest/moniker/struct.Embed.html
[`Nest>`]: https://docs.rs/moniker/latest/moniker/struct.Nest.html
[`Rec>`]: https://docs.rs/moniker/latest/moniker/struct.Rec.html## Roadmap
Moniker is currently good enough to use for initial language prototypes, but
there is more work that we'd like to do to make it a more fully-featured
name binding and scope handling toolkit.- [ ] Initial implementation using a locally nameless representation
- [x] Implement basic type combinators
- [x] `Embed`
- [x] `Ignore`
- [x] `Nest`
- [x] `Rec`
- [x] `Scope`
- [ ] Automatically derive traits
- [x] `BoundTerm`
- [x] `BoundPattern`
- [ ] `Subst`
- [ ] Allow derives to use identifier types other than `String`
- [ ] Implement namespaced variables and binders
- [ ] Performance optimizations
- [ ] Cache max-depth of terms
- [ ] Cache free variables of terms
- [ ] Perform multiple-opening/closing
- [ ] Use visitors
- [ ] Explore implementing other name-binding schemes
- [ ] Named with unique indices
- [ ] Scope Graphs
- [ ] ...?## References
Here is a list of interesting references and prior art that was helpful when
building Moniker. Note that it isn't necessary to read and understand these to
use the library, but they _might_ be useful if you would like to contribute!### Papers
- [The Locally Nameless Representation](https://www.chargueraud.org/research/2009/ln/main.pdf)
- [Binders Unbound](http://www.seas.upenn.edu/~sweirich/papers/icfp11.pdf)
- [An overview of Cαml](http://pauillac.inria.fr/~fpottier/publis/fpottier-alphacaml.pdf)
- [Visitors Unchained](http://gallium.inria.fr/~fpottier/publis/fpottier-visitors-unchained.pdf)
- [Engineering Formal Metatheory](http://www.chargueraud.org/research/2007/binders/binders_popl_08.pdf)### Blog Posts
- [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)
- [Announcing DBLib](http://gallium.inria.fr/blog/announcing-dblib/)
- [Bound](https://www.schoolofhaskell.com/user/edwardk/bound)### Other Libraries
The API was mainly inspired by the [Unbound][unbound] and
[Unbound-Generics][unbound-generics] libraries for Haskell, with some
differences. The main change that we make is to have two separate traits
(`BoundTerm` and `BoundPattern`) in place of Unbound's single `Alpha` type
class. We've found that this better captures the semantics of the library, and
greatly cuts down on the potential for accidental misuse.Other auto-binding libraries exist for a number of different languages:
- [Unbound][unbound]: Specify the binding structure of your data type with an
expressive set of type combinators, and Unbound handles the rest!
Automatically derives alpha-equivalence, free variable calculation,
capture-avoiding substitution, and more.
- [Unbound-Generics][unbound-generics]: an independent re-implementation of
Unbound but using GHC.Generics instead of RepLib.
- [Cαml][alphaCaml]: a tool that turns a so-called "binding specification" into
an OCaml compilation unit.
- [alphaLib][alphaLib]: An OCaml library that helps deal with binding constructs
in abstract syntax trees.
- [abbot][abbot]: Generation of abstract binding trees for SML
- [rabbot][rabbot]: A port of SML's Abbot to Rust
- [DBLib][dblib]: Facilities for working with de Bruijn indices in Coq
- [Bound](https://github.com/ekmett/bound/): DeBruijn indices for Haskell
- [Metalib][metalib]: The Penn Locally Nameless Metatheory Library
- [LibLN][ln]: Locally nameless representation with cofinite quantification[unbound]: https://github.com/sweirich/replib
[unbound-generics]: https://github.com/lambdageek/unbound-generics
[alphaCaml]: http://pauillac.inria.fr/~fpottier/alphaCaml/alphacaml.html.en
[alphaLib]: https://gitlab.inria.fr/fpottier/alphaLib
[abbot]: https://github.com/robsimmons/abbot
[rabbot]: https://github.com/willcrichton/rabbot
[dblib]: https://github.com/coq-contribs/dblib
[metalib]: https://github.com/plclub/metalib
[ln]: http://www.chargueraud.org/softs/ln/## Contributing
We really want to encourage new contributors to help out! Please come chat with
us [on our Gitter channel][gitter-lobby] - if you have any questions about the
project, or just want to say hi!## Acknowledgments
[![YesLogic Logo][yeslogic-logo]][yeslogic]
This work was done in part with the generous support of [YesLogic][yeslogic].
[yeslogic]: http://yeslogic.com/
[yeslogic-logo]: assets/yeslogic-logo.png