├── .gitattributes ├── .gitignore ├── extra └── site-demo.gif ├── src ├── encode.rs ├── parse.lalrpop ├── encode │ ├── function.rs │ ├── numerals.rs │ └── boolean.rs ├── wasm.rs ├── macros.rs ├── lib.rs └── normal.rs ├── examples ├── fv.rs ├── site │ ├── index.html │ ├── package.json │ ├── index.css │ ├── webpack.config.js │ └── index.js ├── y.rs ├── fn.rs ├── encode.rs ├── env.rs ├── macros.rs └── parse.rs ├── benches ├── numerals.rs └── baselines │ ├── v0.3.0_bench.json │ └── v0.4.0_bench.json ├── deploy.sh ├── Cargo.toml ├── LICENSE ├── .travis.yml └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | src/parse.lalrpop linguist-language=rust 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | examples/site/node_modules 4 | 5 | Cargo.lock 6 | -------------------------------------------------------------------------------- /extra/site-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixpulvis/lalrpop-lambda/HEAD/extra/site-demo.gif -------------------------------------------------------------------------------- /src/encode.rs: -------------------------------------------------------------------------------- 1 | // Functions themselves 2 | mod function; 3 | 4 | // Church booleans 5 | mod boolean; 6 | 7 | // Church numerals 8 | mod numerals; 9 | -------------------------------------------------------------------------------- /examples/fv.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_syntax)] 2 | 3 | #[macro_use] 4 | extern crate lalrpop_lambda; 5 | 6 | fn main() { 7 | dbg!(app!(abs!{x.app!(x,y)}, abs!{y.app!(x,y)}).free_variables()); 8 | dbg!(app!(abs!{f.abs!{x.app!(f,x)}}, abs!{x.x}).free_variables()); 9 | } 10 | -------------------------------------------------------------------------------- /examples/site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | λ-calculus 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/y.rs: -------------------------------------------------------------------------------- 1 | #![feature(non_ascii_idents)] 2 | extern crate lalrpop_lambda; 3 | 4 | use lalrpop_lambda::parse::ExpressionParser; 5 | 6 | fn main() { 7 | let parser = ExpressionParser::new(); 8 | 9 | // Make the Y combinator. 10 | println!("ω = {}", parser.parse(r"λx.(x x)").unwrap()); 11 | println!("Ω = {}", parser.parse(r"(λx.(x x)) (λx.(x x))").unwrap()); 12 | println!("W = {}", parser.parse(r"λf.λx. f x x").unwrap()); 13 | println!("Y = {}", parser.parse(r"λf.(λx.f (x x)) (λx.f (x x))").unwrap()); 14 | } 15 | -------------------------------------------------------------------------------- /examples/site/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "serve": "webpack-dev-server" 4 | }, 5 | "dependencies": { 6 | "@babel/core": "^7.9.0", 7 | "@babel/preset-env": "^7.9.0", 8 | "@babel/preset-react": "^7.9.4", 9 | "babel-loader": "^8.1.0", 10 | "lalrpop-lambda": "file:../../pkg", 11 | "react": "^16.13.1", 12 | "react-dom": "^16.13.1" 13 | }, 14 | "devDependencies": { 15 | "babel-plugin-syntax-dynamic-import": "^6.18.0", 16 | "webpack": "^4.42.1", 17 | "webpack-cli": "^3.3.11", 18 | "webpack-dev-server": "^3.10.3" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/fn.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_syntax)] 2 | 3 | #[macro_use] 4 | extern crate lalrpop_lambda; 5 | 6 | use lalrpop_lambda::Expression; 7 | 8 | fn main() { 9 | let two = abs!{f.abs!{x.app!(var!(f), app!(var!(f), var!(x)))}}; 10 | println!("{}", two(var!(x))(var!(x))); 11 | println!("{}", var!(x)(var!(y))); 12 | println!("{}", app!(var!(x),var!(y))(var!(z))); 13 | 14 | println!("{:?}", λ!{x.x}(1)); 15 | 16 | let id: fn(u64) -> u64 = |x| x; 17 | println!("{}", Expression::from(id)); 18 | let f = u64>::from(abs!{x.x}); 19 | println!("{}", Expression::from(f)); 20 | } 21 | -------------------------------------------------------------------------------- /examples/site/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 10px 20px; 3 | } 4 | 5 | .inputs * { 6 | display: block; 7 | } 8 | 9 | textarea { 10 | border: 2px dashed black; 11 | color: black; 12 | font-size: 16px; 13 | font-family: monospace; 14 | height: 3in; 15 | overflow: auto; 16 | width: 100%; 17 | margin-bottom: 5px; 18 | } 19 | 20 | table { 21 | border-spacing: 5px 0; 22 | } 23 | 24 | table th { 25 | text-align: right; 26 | } 27 | 28 | code { 29 | padding: 0 3px; 30 | background: #ddd; 31 | font-size: 16px; 32 | } 33 | 34 | p strong + code { 35 | margin-left: 3px; 36 | } 37 | 38 | code.error { 39 | background: #f25; 40 | } 41 | -------------------------------------------------------------------------------- /examples/site/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | module.exports = { 3 | mode: "development", 4 | entry: "./index.js", 5 | output: { 6 | path: path.resolve(__dirname, "dist"), 7 | filename: "index.js", 8 | }, 9 | module: { 10 | rules: [ 11 | { 12 | test: /.(js|jsx)$/, 13 | exclude: /node_modules/, 14 | use: { 15 | loader: 'babel-loader', 16 | options: { 17 | presets: ['@babel/preset-env', 18 | '@babel/preset-react'], 19 | plugins: ['syntax-dynamic-import'] 20 | } 21 | } 22 | } 23 | ] 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /benches/numerals.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | extern crate lalrpop_lambda; 4 | 5 | use criterion::Criterion; 6 | use lalrpop_lambda::Expression; 7 | 8 | fn compare_benchmark(c: &mut Criterion) { 9 | c.bench_function_over_inputs("native addition", |b, &n| { 10 | b.iter(|| { 11 | n + n 12 | }) 13 | }, &[0,1,2,4,8,16,32]); 14 | 15 | c.bench_function_over_inputs("λ-expression addition", |b, &n| { 16 | b.iter(|| { 17 | let e = Expression::from(*n); 18 | u64::from(e.clone() + e) 19 | }) 20 | }, &[0,1,2,4,8,16,32]); 21 | } 22 | 23 | criterion_group!(benches, compare_benchmark); 24 | criterion_main!(benches); 25 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get the current git revision short name. 4 | rev=$(git rev-parse --short HEAD) 5 | 6 | # Assume the docs are already built... 7 | cd target/doc 8 | 9 | # Create a new clone of the git repository here. 10 | git init 11 | # TODO: Deploy as a deployer, or others? 12 | git config user.name "Nathan Lilienthal" 13 | git config user.email "nathan@nixpulvis.com" 14 | # Create a remote to the GitHub repository. 15 | git remote add upstream "https://$GH_TOKEN@github.com/nixpulvis/lalrpop-lambda" 16 | # Fetch, and checkout to the GitHub Pages branch. 17 | git fetch upstream && git reset upstream/gh-pages 18 | 19 | touch . 20 | git add -A . 21 | 22 | # Commit the new build. 23 | git commit -m "rebuild pages at ${rev}" 24 | # Push the new build. 25 | git push -q upstream HEAD:gh-pages 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lalrpop-lambda" 3 | description = "A λ-calculus grammar writting with LALRPOP." 4 | repository = "https://github.com/nixpulvis/lalrpop-lambda" 5 | version = "0.6.1" 6 | authors = ["Nathan Lilienthal "] 7 | license = "MIT" 8 | edition = "2018" 9 | build = "build.rs" 10 | 11 | [features] 12 | default = ["wasm"] 13 | wasm = ["wasm-bindgen"] 14 | 15 | [lib] 16 | crate-type = ["rlib", "cdylib"] 17 | 18 | [dependencies] 19 | lalrpop-util = "0.18.1" 20 | regex = "1.3.6" 21 | wasm-bindgen = { version = "0.2.60", optional = true } 22 | pretty_assertions = "0.6.1" 23 | 24 | [build-dependencies] 25 | lalrpop = { version = "0.18.1", features = ["lexer"] } 26 | 27 | [dev-dependencies] 28 | criterion = "0.3.1" 29 | 30 | [[bench]] 31 | name = "numerals" 32 | harness = false 33 | -------------------------------------------------------------------------------- /examples/encode.rs: -------------------------------------------------------------------------------- 1 | #![feature(non_ascii_idents, box_syntax)] 2 | extern crate lalrpop_lambda; 3 | 4 | use lalrpop_lambda::Expression; 5 | 6 | fn main() { 7 | let n = 0; 8 | let ln = Expression::from(n); 9 | let nn = u64::from(ln.clone()); 10 | println!("{} -> {} -> {}", n, ln, nn); 11 | 12 | let n = 1; 13 | let ln = Expression::from(n); 14 | let nn = u64::from(ln.clone()); 15 | println!("{} -> {} -> {}", n, ln, nn); 16 | 17 | let n = 5; 18 | let ln = Expression::from(n); 19 | let nn = u64::from(ln.clone()); 20 | println!("{} -> {} -> {}", n, ln, nn); 21 | 22 | let t = true; 23 | let lt = Expression::from(t); 24 | let tt = bool::from(lt.clone()); 25 | println!("{} -> {} -> {}", t, lt, tt); 26 | 27 | let f = false; 28 | let lf = Expression::from(f); 29 | let ff = bool::from(lf.clone()); 30 | println!("{} -> {} -> {}", f, lf, ff); 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Nathan Lilienthal 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /examples/env.rs: -------------------------------------------------------------------------------- 1 | #![feature(non_ascii_idents, box_syntax)] 2 | 3 | #[macro_use] 4 | extern crate lalrpop_lambda; 5 | 6 | use std::collections::HashMap; 7 | use lalrpop_lambda::Strategy; 8 | 9 | macro_rules! resolve { 10 | ($expr:expr, $env:expr) => { 11 | println!("{} -r> {} -> {}", 12 | $expr, 13 | $expr.resolve($env), 14 | $expr.resolve($env).normalize(&Strategy::Applicative(false))); 15 | } 16 | } 17 | 18 | fn main() { 19 | let mut env = HashMap::new(); 20 | env.insert(variable!(i), abs!{x.x}); 21 | env.insert(variable!(n), 1.into()); 22 | env.insert(variable!(x), var!(x)); 23 | for (v, e) in &env { 24 | println!("{} := {}", v, e); 25 | } 26 | resolve!(var!(n), &env); 27 | resolve!(var!(x), &env); 28 | resolve!(var!(q), &env); 29 | resolve!(abs!{a.a}, &env); 30 | resolve!(abs!{a.n}, &env); 31 | resolve!(app!(a,n), &env); 32 | resolve!(app!(n,a), &env); 33 | resolve!(app!(n,n), &env); 34 | resolve!(app!(i,n), &env); 35 | } 36 | -------------------------------------------------------------------------------- /src/parse.lalrpop: -------------------------------------------------------------------------------- 1 | use crate::{Expression, Variable}; 2 | 3 | grammar; 4 | 5 | pub Variable: Variable = { 6 | Id => Variable(<>, None), 7 | ":" => Variable(id, Some(ty)), 8 | }; 9 | 10 | pub Expression: Expression = { 11 | Abstraction => <>, 12 | Application => <>, 13 | } 14 | 15 | Abstraction: Expression = { 16 | => { 17 | let body = match term { 18 | Some((_, o @ Some(_))) => o, 19 | _ => None, 20 | }; 21 | Expression::build_abs(ls.len(), ids, body) 22 | }, 23 | } 24 | 25 | Application: Expression = { 26 | // NOTE: Base case of terminals is here to allow the `Application` 27 | // production to be left associative. 28 | Terminal => <>, 29 | => app!({e1},{e2}), 30 | } 31 | 32 | Terminal: Expression = { 33 | Variable => Expression::Var(<>), 34 | "(" ")" => e, 35 | } 36 | 37 | Lambda = { 38 | "λ", 39 | "\\", 40 | } 41 | 42 | Id: String = r"[a-zA-Z0-9-_]+" => { 43 | <>.to_string() 44 | }; 45 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | 8 | matrix: 9 | allow_failures: 10 | - rust: stable 11 | - rust: beta 12 | 13 | script: 14 | - cargo test 15 | - cargo doc --no-deps 16 | 17 | after_success: 18 | # TODO: PR/branch docs. 19 | - test $TRAVIS_PULL_REQUEST == "false" && 20 | test $TRAVIS_BRANCH == "master" && 21 | test $TRAVIS_ALLOW_FAILURE == "false" && 22 | bash deploy.sh 23 | 24 | env: 25 | global: 26 | secure: Qg/smWvkCJGLDb42Mh61qzoLiXqbxfGcIp98JGTml8M194bCBxzMJFVomYE2h8Hz4Oc+opW6phWR2yS9OTy04Uf6c/Y+DDBrSj2F+J0wn6+1u0MktzGjLHvz9Nv+7FFp5gNcUAbUOkc2VptbiBype4hL/VUnbhAnu/cDxUvugOfYpdJSCn8cAAqCT1v7K8NOFLDJDzH3vFcNM+VNFVFyLQP/1p3rJExECYtE8/75QPqGDWeHhA6g/OZ/h3203xrhaopocVVA1aATp8K7719Fy7Aiqf/hArU+WFJZ/kBKm4Eng4m/8T5ZsFbJX2qngFvryKvloELKSWuT1QFPFK81lfOHcBb+gQV8BMihpOs7UErOXFZPjyYPyXjgPcAZ9QTvsEnD1E2PED/S5F5s4ZmpTcwdI9w0A5ZDYaqbcMJ0dfOIyppQrWun2i62qdcMaP/gxMn3csF1aw7Xu6gAQvhiuyWWEDYcmv0mgn33cUTeT+bDAVMSFfATQ4CHkom5oj00V04Ep3OhJDs5VEi0+3/uz2UodxTM+XgSlbzMK3n/sLzb6jyqChGboVjhHGyLjbPZE2ZflkLO3+nMg1Ub5UT4deHK8GOpDjiyPNUL5pwFwFWZiR4nbf94ISucGaNE59MU3oTSiJuqKWDJ/Xnhh9/yvAzDubuDZvBPQ008in9fscQ= 27 | -------------------------------------------------------------------------------- /examples/macros.rs: -------------------------------------------------------------------------------- 1 | #![feature(non_ascii_idents, box_syntax)] 2 | 3 | #[macro_use] 4 | extern crate lalrpop_lambda; 5 | 6 | use lalrpop_lambda::Strategy; 7 | 8 | fn main() { 9 | // Both the short `λ!` macro, as well as the ASCII `abs!` macro. 10 | println!("{} = {}", λ!{x.x}, abs!{x.x}); 11 | 12 | // Mix in rust bindings with lambda calculus macros using `{binding}`. 13 | let ω = abs!{x.app!(x,x)}; 14 | println!("ω: {}", ω); 15 | // Doesn't do what you might want. 16 | println!("app!(ω,x): {}", app!(ω,x)); 17 | // But this does ;) 18 | println!("app!({{&ω}},x): {} -> {}", 19 | app!({&ω},x), 20 | app!({&ω},x).normalize(&Strategy::Applicative(false))); 21 | 22 | // An empty church numeral. 23 | let zero = λ!{f.λ!{x.x}}; 24 | println!("0: {}", &zero); 25 | 26 | // A single church numeral. 27 | let one = λ!{f.λ!{x.γ!(f, x)}}; 28 | println!("1: {}", &one); 29 | 30 | // Identity application. 31 | let id = λ!{x.x}; 32 | println!("(id one): {} -> {}", 33 | app!({&id}, {&one}), 34 | app!({&id}, {&one}).normalize(&Strategy::Applicative(false))); 35 | 36 | let two = abs!{f.abs!{x.app!(var!(f), 37 | app!(app!(abs!{f.abs!{x.app!(var!(f), 38 | var!(x))}}, 39 | var!(f)),var!(x)))}}; 40 | println!("two: {} -> {}", 41 | two, 42 | two.normalize(&Strategy::Applicative(false))); 43 | 44 | // Mmmmm, curry. 45 | println!("{}", abs!{x y.app!(x,y)}); 46 | println!("{}", abs!{.abs!{.abs!{.var!("")}}}); 47 | 48 | // Try out a type. 49 | println!("{}", abs!{x:t.x}); 50 | } 51 | -------------------------------------------------------------------------------- /examples/parse.rs: -------------------------------------------------------------------------------- 1 | #![feature(non_ascii_idents)] 2 | extern crate lalrpop_lambda; 3 | 4 | use lalrpop_lambda::{Expression, Strategy}; 5 | use lalrpop_lambda::parse::ExpressionParser; 6 | 7 | macro_rules! parse { 8 | ($expr:expr $(, $func:expr)?) => {{ 9 | let e = ExpressionParser::new().parse($expr).unwrap(); 10 | print!("{} parse-> {}", $expr, e); 11 | $( 12 | let e = $func(&e, &Strategy::Applicative(false)); // very funky. 13 | print!(" -> {}", e); 14 | )? 15 | println!(""); 16 | e 17 | }} 18 | } 19 | 20 | fn main() { 21 | parse!("x"); 22 | parse!(r"\x.x"); 23 | parse!(r"\x.y"); 24 | parse!("x x"); 25 | parse!("x y"); 26 | 27 | // A type! 28 | parse!(r"\x:t.x x"); 29 | 30 | println!(); 31 | parse!(r"\\\x y.x y"); 32 | parse!(r"\x y.x y"); 33 | parse!(r"\\\"); 34 | println!(); 35 | 36 | println!(); 37 | parse!(r"(\x.x) x", Expression::normalize); 38 | parse!(r"(\x.x) y", Expression::normalize); 39 | 40 | // Single β-reduction identity function. 41 | println!(); 42 | parse!(r"\x.x a", Expression::normalize); 43 | parse!(r"(\x.x) a", Expression::normalize); 44 | 45 | // Partial application. 46 | println!(); 47 | let norm = parse!(r"(\x.\y.x y) a", Expression::normalize); 48 | parse!(&format!("({}) b", norm), Expression::normalize); 49 | // Multiple (curried) β-reductions on an identity function. 50 | parse!(r"(\x.\y.x y) a b", Expression::normalize); 51 | 52 | println!(); 53 | parse!(r"((\x.(\x.x x) a) b)", Expression::normalize); 54 | 55 | // Ω 56 | println!(); 57 | parse!(r"\x.(x x) (\x.(x x))"); 58 | parse!(r"(\x.(x x)) (\x.(x x))"); 59 | // XXX: Blows the stack in our strategy. 60 | parse!(r"(\x.(x x)) (\x.(x x))", Expression::normalize); 61 | } 62 | -------------------------------------------------------------------------------- /src/encode/function.rs: -------------------------------------------------------------------------------- 1 | use crate::{Expression, Abstraction}; 2 | 3 | /// Function call support for an `Expression`. 4 | /// 5 | /// ``` 6 | /// # #![feature(box_syntax)] 7 | /// # #[macro_use] 8 | /// # extern crate lalrpop_lambda; 9 | /// # fn main() { 10 | /// assert_eq!(0u64, λ!{x.x}(0).into()); 11 | /// assert_eq!(γ!(γ!(a,b),0), γ!(a,b)(0)); 12 | /// # } 13 | /// ``` 14 | impl FnOnce<(T,)> for Expression 15 | where T: Into + 16 | From 17 | { 18 | // TODO: Return Option here too. 19 | type Output = Expression; 20 | 21 | extern "rust-call" fn call_once(self, t: (T,)) -> Expression { 22 | γ!({self},t.0.into()) 23 | } 24 | } 25 | 26 | impl From for fn(u64) -> u64 { 27 | fn from(e: Expression) -> Self { 28 | match e { 29 | Expression::Abs(Abstraction(ref lid, box ref e1)) => { 30 | match e1 { 31 | Expression::Var(ref rid) if lid == rid => { 32 | |x| x 33 | }, 34 | Expression::Var(_) => { 35 | |_| 0 36 | }, 37 | _ => unreachable!(), 38 | } 39 | }, 40 | _ => |_| panic!("not a function"), 41 | } 42 | } 43 | } 44 | 45 | impl From u64> for Expression { 46 | fn from(_f: fn(u64) -> u64) -> Self { 47 | // TODO: Since we can't call `f` now, we should bind it to an `env` and 48 | // add a reference to f here. 49 | // 50 | // un-η 51 | abs!{x.app!(f,x)} 52 | } 53 | } 54 | 55 | 56 | #[cfg(test)] 57 | mod tests { 58 | use pretty_assertions::assert_eq; 59 | 60 | #[test] 61 | fn var() { 62 | let one = abs!{f.abs!{x.app!(f,x)}}; 63 | assert_eq!(app!(x,{one}), var!(x)(1)); 64 | } 65 | 66 | #[test] 67 | fn abs() { 68 | assert_eq!(5u64, abs!{x.x}(5).into()); 69 | } 70 | 71 | #[test] 72 | fn app() { 73 | let zero = abs!{f.abs!{x.x}}; 74 | assert_eq!(app!(app!(a,b),{zero}), app!(a,b)(0)); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # λ-calculus Parser (using LALRPOP) 2 | 3 | [![Build Status](https://travis-ci.org/nixpulvis/lalrpop-lambda.svg?branch=master)](https://travis-ci.org/nixpulvis/lalrpop-lambda) 4 | [![Crates.io Version](https://img.shields.io/crates/v/lalrpop-lambda.svg?color=%238035b9)](https://crates.io/crates/lalrpop-lambda) 5 | [![docs.rs](https://img.shields.io/badge/docs.rs-0.x.x-lightgrey.svg)](https://docs.rs/lalrpop-lambda) 6 | 7 | Write lambda calculus with ease, and evaluate it. There are a number of ways to 8 | use this library (each interchangeable with another): 9 | 10 | - `Expression` AST variants `Abs`, `App`, and `Var` 11 | - Macros `abs!`/`λ!`, `app!`/`γ!`, and `var!` 12 | 13 | ```rust 14 | let id = λ!{x.x}; 15 | let one = λ!{f.λ!{x.γ!(f,x)}}; 16 | assert_eq!(1u64, u64::from(app!({id},{one}))); 17 | ``` 18 | 19 | - Parsed λ-calculus strings 20 | 21 | ```rust 22 | let parser = ExpressionParser::new(); 23 | parser.parse(r"\a b.a"); 24 | parser.parse(r"\f x.(f (f x))"); 25 | parser.parse(r"\\\x y z"); 26 | ``` 27 | 28 | - Native types: `u64`, `bool`, `fn` (WIP) 29 | 30 | ```rust 31 | assert_eq!(λ!{f.λ!{x.γ!(f,γ!(f,x))}}, Expression::from(2u64)); 32 | assert_eq!(true, bool::from(λ!{a.λ!{b.a}})); 33 | assert_eq!(1, λ!{x.x}(1)); 34 | ``` 35 | 36 | ![](extra/site-demo.gif) 37 | 38 | The above is generated with `wasm-pack`, see [example/site][example/site]. 39 | 40 | ### Usage (Rust) 41 | 42 | ```toml 43 | [dependencies] 44 | lalrpop_lambda = "*" 45 | ``` 46 | 47 | Read the [Rust documentation](https://docs.rs/lalrpop-lambda) for more 48 | information. 49 | 50 | ### Usage (WASM/JS) 51 | 52 | An `Exp` structure is provided through WASM for use in JS. This allows cross 53 | platform, client-side, web based interfaces to be built for the λ-calculus. 54 | 55 | Read the [WASM documentation][lalrpop_lambda-wasm] for more information. 56 | 57 | ### Development 58 | 59 | This assumes you have an updated and working copy of [`rustup`][rustup]. 60 | 61 | ```sh 62 | cargo +nightly [build | test | bench | doc | run --example <>] 63 | ``` 64 | 65 | ##### WASM 66 | 67 | First make sure you have `wasm-pack` installed. Then: 68 | 69 | ``` 70 | wasm-pack build 71 | cd examples/site 72 | npm run serve 73 | ``` 74 | 75 | [example/site]: https://github.com/nixpulvis/lalrpop-lambda/blob/master/examples/site/index.js 76 | [lalrpop_lambda-wasm]: https://docs.rs/lalrpop-lambda/latest/lalrpop_lambda/wasm/index.html 77 | -------------------------------------------------------------------------------- /examples/site/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import("./node_modules/lalrpop-lambda/lalrpop_lambda.js").then(wasm => { 5 | class LambdaEditor extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { input: '', expression: null, error: null }; 9 | this.handleChange = this.handleChange.bind(this); 10 | } 11 | 12 | handleChange(event) { 13 | let input = event.target.value; 14 | 15 | try { 16 | let expression = new wasm.Exp(input); 17 | this.setState({ input, expression, error: null }); 18 | } catch(e) { 19 | this.setState({ input, expression: null, error: e }); 20 | } 21 | } 22 | 23 | render() { 24 | if (this.state.input === '') { 25 | var display = ( 26 |

27 | Input a valid λ-expression, e.g. 28 | \x.x x 29 |

30 | ); 31 | } else if (this.state.error) { 32 | var display = 33 | } else { 34 | var display = 35 | } 36 | 37 | return ( 38 |
39 | 41 | {display} 42 |
43 | ); 44 | } 45 | } 46 | 47 | class LambdaOutputs extends React.Component { 48 | render() { 49 | let outputs = [ 50 | { label: 'parse', func: 'toString' }, 51 | { label: 'app ->', func: 'applicative' }, 52 | { label: 'cbv ->', func: 'call_by_value' }, 53 | { label: 'norm ->', func: 'normal' }, 54 | { label: 'cbn ->', func: 'call_by_name' }, 55 | // TODO: Hybrid by-func and applicative. 56 | { label: 'spine ->', func: 'head_spine' }, 57 | // TODO: Hybrid head spine and normal. 58 | { label: "numeral =", func: 'toNumber' }, 59 | { label: "bool =", func: 'toBool' }, 60 | ]; 61 | return ( 62 | 63 | 64 | {outputs.map((o, i) => { 65 | return (); 69 | })} 70 | 71 |
72 | ) 73 | } 74 | } 75 | 76 | class LambdaOutput extends React.Component { 77 | render() { 78 | if (this.props.func) { 79 | try { 80 | var result = ( 81 | {this.props.exp[this.props.func]().toString()} 82 | ); 83 | } catch(e) { 84 | var result = {e.toString()}; 85 | } 86 | } else { 87 | var result = ''; 88 | } 89 | return ( 90 | 91 | {this.props.label} 92 | {result} 93 | 94 | ); 95 | } 96 | } 97 | 98 | class LambdaParseError extends React.Component { 99 | render() { 100 | return ( 101 |

102 | {this.props.message} 103 |

104 | ) 105 | } 106 | } 107 | 108 | ReactDOM.render(, document.getElementById('mount')); 109 | }); 110 | -------------------------------------------------------------------------------- /src/wasm.rs: -------------------------------------------------------------------------------- 1 | //! WASM types for use in JS. 2 | //! 3 | //! In addition to the Rust [crate][TODO], the WASM package is published to NPM 4 | //! as well. If you are only interested in using this library from JS, this should 5 | //! be all you need. 6 | //! 7 | //! ### Install 8 | //! 9 | //! ```sh 10 | //! npm install lalrpop-lambda [--save] 11 | //! ``` 12 | //! 13 | //! Once this module is compiled to WASM, it must be loaded. Read more about [Loading and running 14 | //! WebAssembly code](https://developer.mozilla.org/en-US/docs/WebAssembly/Loading_and_running). 15 | //! 16 | //! ```js 17 | //! const module_path = "./node_modules/lalrpop-lambda/lalrpop_lambda.js"; 18 | //! import(module_path).then(lambda => { ... }); 19 | //! ``` 20 | //! 21 | //! See `examples/site` for more. 22 | use wasm_bindgen::prelude::*; 23 | use crate::{parse, Expression}; 24 | use crate::normal::Strategy; 25 | 26 | /// A parsed λ-expression 27 | /// 28 | /// This struct is a wrapper around an [`Expression`] to both allow exporting it to JS, and to 29 | /// contain functions functions we want to export for the `Exp`, for example [`Exp::to_string`]. 30 | #[wasm_bindgen] 31 | pub struct Exp(Expression); 32 | 33 | #[wasm_bindgen] 34 | impl Exp { 35 | /// Parse and construct a new `Expr` 36 | /// 37 | /// ```js 38 | /// new lambda.Exp("(\\x.x x) y"); 39 | /// new lambda.Exp(2); 40 | /// new lambda.Exp(false); 41 | /// new lambda.Exp("*wtf"); // Throws exception. 42 | /// ``` 43 | #[wasm_bindgen(constructor)] 44 | pub fn new(v: JsValue) -> Result { 45 | if let Some(s) = v.as_string() { 46 | let parser = parse::ExpressionParser::new(); 47 | match parser.parse(&s) { 48 | Ok(e) => Ok(Exp(e)), 49 | Err(e) => Err(JsValue::from_str(&format!("{}", e))), 50 | } 51 | } else if let Some(n) = v.as_f64() { 52 | Ok(Exp(Expression::from(n as u64))) 53 | } else if let Some(b) = v.as_bool() { 54 | Ok(Exp(Expression::from(b))) 55 | } else { 56 | Err(JsValue::from_str("invalid constructor type")) 57 | } 58 | } 59 | 60 | 61 | pub fn applicative(&self, η: bool) -> Self { 62 | Exp(self.0.normalize(&Strategy::Applicative(η))) 63 | } 64 | 65 | pub fn call_by_value(&self) -> Self { 66 | Exp(self.0.normalize(&Strategy::CallByValue)) 67 | } 68 | 69 | pub fn normal(&self, η: bool) -> Self { 70 | Exp(self.0.normalize(&Strategy::Normal(η))) 71 | } 72 | 73 | pub fn call_by_name(&self) -> Self { 74 | Exp(self.0.normalize(&Strategy::CallByName)) 75 | } 76 | 77 | pub fn head_spine(&self, η: bool) -> Self { 78 | Exp(self.0.normalize(&Strategy::HeadSpine(η))) 79 | } 80 | 81 | 82 | /// See [`std::fmt::Display`] 83 | /// 84 | /// ```js 85 | /// let expr = new lambda.Exp("\\x.x x"); 86 | /// console.log(`${expr}`); 87 | /// ``` 88 | #[wasm_bindgen(method, js_name = toString)] 89 | pub fn to_string(&self) -> String { 90 | format!("{}", self.0) 91 | } 92 | 93 | /// See `From for u64` 94 | /// 95 | /// ```js 96 | /// let two = new lambda.Exp("\\f.\\x.(f (f x))"); 97 | /// console.log(`${two.toNumber()}`); 98 | /// ``` 99 | #[wasm_bindgen(method, js_name = toNumber)] 100 | pub fn to_number(&self) -> usize { 101 | u64::from(self.0.clone()) as usize 102 | } 103 | 104 | /// See `From for bool` 105 | /// 106 | /// 107 | /// ```js 108 | /// let t = new lambda.Exp("\\a.\\b.a"); 109 | /// console.log(`${t.toBool()}`); 110 | /// ``` 111 | #[wasm_bindgen(method, js_name = toBool)] 112 | pub fn to_bool(&self) -> bool { 113 | self.0.clone().into() 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/encode/numerals.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Add, Mul}; 2 | use crate::{Expression, Abstraction, Application, Variable}; 3 | use crate::normal::Strategy; 4 | 5 | /// Church encoded natural numbers 6 | /// 7 | /// ``` 8 | /// # #![feature(box_syntax)] 9 | /// # #[macro_use] 10 | /// # extern crate lalrpop_lambda; 11 | /// use lalrpop_lambda::Expression; 12 | /// 13 | /// # fn main() { 14 | /// assert_eq!(λ!{f.λ!{x.x}}, Expression::from(0)); 15 | /// assert_eq!(λ!{f.λ!{x.γ!(f,x)}}, Expression::from(1)); 16 | /// assert_eq!(λ!{f.λ!{x.γ!(f,γ!(f,γ!(f,x)))}}, Expression::from(3)); 17 | /// # } 18 | /// ``` 19 | impl From for Expression { 20 | fn from(n: u64) -> Self { 21 | let succ = λ!{n.λ!{f.λ!{x.γ!(f, γ!(γ!(n, f), x))}}}; 22 | let mut e = λ!{f.λ!{x.x}}; 23 | for _ in 0..n { 24 | e = app!({&succ}, {&e}).normalize(&Strategy::Applicative(false)); 25 | } 26 | e 27 | } 28 | } 29 | 30 | /// Convert λ term back to native Rust type 31 | /// 32 | /// ``` 33 | /// # #![feature(box_syntax)] 34 | /// # #[macro_use] 35 | /// # extern crate lalrpop_lambda; 36 | /// # fn main() { 37 | /// assert_eq!(0, u64::from(λ!{f.λ!{x.x}})); 38 | /// assert_eq!(1, u64::from(λ!{f.λ!{x.γ!(f,x)}})); 39 | /// assert_eq!(3, u64::from(λ!{f.λ!{x.γ!(f,γ!(f,γ!(f,x)))}})); 40 | /// # } 41 | /// ``` 42 | impl From for u64 { 43 | fn from(e: Expression) -> u64 { 44 | // TODO: It would be ideal to use the Fn conversion and a way to "bind" `f` to u64::add. 45 | // 46 | // XXX: In fact more than ideal, this really should only be able to return an `Option` 47 | // since there are lambda terms which can evaluate to something which is not a chruch 48 | // encoded function. 49 | match e.normalize(&Strategy::Applicative(true)) { 50 | Expression::Var(id) => { 51 | if id == variable!(f) { 1 } else { 0 } 52 | }, 53 | Expression::Abs(Abstraction(Variable(_id, _ty), box body)) => { 54 | u64::from(body) 55 | }, 56 | Expression::App(Application(box e1, box e2)) => { 57 | u64::from(e1) + u64::from(e2) 58 | }, 59 | } 60 | } 61 | } 62 | 63 | /// ``` 64 | /// # #![feature(box_syntax)] 65 | /// # #[macro_use] 66 | /// # extern crate lalrpop_lambda; 67 | /// # fn main() { 68 | /// let one = λ!{f.λ!{x.γ!(f,x)}}; 69 | /// let two = one.clone() + one.clone(); 70 | /// assert_eq!(2, u64::from(two.clone())); 71 | /// assert_eq!(4, u64::from(two.clone() + two.clone())); 72 | /// # } 73 | /// ``` 74 | impl Add for Expression { 75 | type Output = Self; 76 | 77 | fn add(self, other: Self) -> Self { 78 | let add = λ!{m.λ!{n.λ!{f.λ!{x.γ!(γ!(m,f),γ!(γ!(n,f),x))}}}}; 79 | γ!(γ!({add},{self}),{other}).normalize(&Strategy::Applicative(false)) 80 | } 81 | } 82 | 83 | /// ``` 84 | /// # #![feature(box_syntax)] 85 | /// # #[macro_use] 86 | /// # extern crate lalrpop_lambda; 87 | /// # fn main() { 88 | /// let one = λ!{f.λ!{x.γ!(f,x)}}; 89 | /// let two = one.clone() + one.clone(); 90 | /// assert_eq!(1, u64::from(one.clone() * one.clone())); 91 | /// assert_eq!(4, u64::from(two.clone() * two.clone())); 92 | /// # } 93 | /// ``` 94 | impl Mul for Expression { 95 | type Output = Self; 96 | 97 | fn mul(self, other: Self) -> Self { 98 | let mul = λ!{m.λ!{n.λ!{f.λ!{x.γ!(γ!(m,γ!(n,f)),x)}}}}; 99 | γ!(γ!({mul},{self}),{other}).normalize(&Strategy::Applicative(false)) 100 | } 101 | } 102 | 103 | 104 | #[cfg(test)] 105 | mod tests { 106 | use pretty_assertions::assert_eq; 107 | use crate::parse::ExpressionParser; 108 | use super::*; 109 | 110 | // // TODO: Move these to tests as we finalize. 111 | // dbg!(u64::from(var!(x))); 112 | // dbg!(u64::from(var!(f))); 113 | // dbg!(u64::from(app!(f,app!(f,x)))); 114 | // dbg!(u64::from(abs!{f.app!(f,app!(f,x))})); 115 | // dbg!(u64::from(abs!{f.abs!{x.app!(f,app!(f,x))}})); 116 | 117 | #[test] 118 | fn u64() { 119 | assert_eq!(0u64, Expression::from(0).into()); 120 | assert_eq!(5u64, Expression::from(5).into()); 121 | } 122 | 123 | #[test] 124 | fn zero() { 125 | // TODO: Should this be correct? What to do about smaller terms? 126 | assert_eq!(0, u64::from(λ!{x.x})); 127 | } 128 | 129 | 130 | #[test] 131 | fn one() { 132 | let ω = ExpressionParser::new().parse("λx.x x").unwrap(); 133 | 134 | // TODO: Should this be correct? What to do about smaller terms? 135 | assert_eq!(1, u64::from(ω(Expression::from(1)))); 136 | } 137 | 138 | #[test] 139 | fn add() { 140 | assert_eq!(Expression::from(5), Expression::from(2) + 141 | Expression::from(3)); 142 | } 143 | 144 | #[test] 145 | fn multiply() { 146 | assert_eq!(Expression::from(6), Expression::from(2) * Expression::from(3)); 147 | } 148 | 149 | // app!(n, (\f.\x.(f (f x)))) -> 2^n 150 | } 151 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | /// A raw `Variable` 2 | /// 3 | /// ``` 4 | /// # #[macro_use] 5 | /// # extern crate lalrpop_lambda; 6 | /// # fn main() { 7 | /// let x = variable!(x); 8 | /// # } 9 | /// ``` 10 | #[macro_export] 11 | macro_rules! variable { 12 | ($b:ident) => {{ 13 | $crate::Variable(stringify!($b).into(), None) 14 | }}; 15 | ($b:expr) => {{ 16 | $crate::Variable($b.into(), None) 17 | }}; 18 | ($b:ident, $ty:ident) => {{ 19 | $crate::Variable(stringify!($b).into(), Some(stringify!($ty).into())) 20 | }}; 21 | ($b:expr, $ty:ident) => {{ 22 | $crate::Variable($b.into(), Some(stringify!($ty).into())) 23 | }}; 24 | } 25 | 26 | /// A variable (`Var`) expression 27 | #[macro_export] 28 | macro_rules! var { 29 | ($b:ident) => {{ 30 | $crate::Expression::Var(variable!($b)) 31 | }}; 32 | ($b:expr) => {{ 33 | $crate::Expression::Var(variable!($b)) 34 | }}; 35 | } 36 | 37 | /// An abstraction (`Abs`) expression 38 | #[macro_export] 39 | macro_rules! abs { 40 | {. $body:ident} => {{ 41 | $crate::Expression::build_abs(1, vec![], Some(var!($body))) 42 | }}; 43 | {. $body:expr} => {{ 44 | $crate::Expression::build_abs(1, vec![], Some($body.into())) 45 | }}; 46 | {$($arg:ident : $ty:ident)* . $body:ident} => {{ 47 | let mut ids: Vec<$crate::Variable> = Vec::new(); 48 | $(ids.push(variable!($arg, $ty));)* 49 | $crate::Expression::build_abs(1, ids, Some(var!($body))) 50 | }}; 51 | {$($arg:ident)* . $body:ident} => {{ 52 | let mut ids: Vec<$crate::Variable> = Vec::new(); 53 | $(ids.push(variable!($arg));)* 54 | $crate::Expression::build_abs(1, ids, Some(var!($body))) 55 | }}; 56 | {$($arg:ident : $ty:ident)* . $body:expr} => {{ 57 | let mut ids: Vec<$crate::Variable> = Vec::new(); 58 | $(ids.push(variable!($arg, $ty));)* 59 | $crate::Expression::build_abs(1, ids, Some($body.into())) 60 | }}; 61 | {$($arg:ident)* . $body:expr} => {{ 62 | let mut ids: Vec<$crate::Variable> = Vec::new(); 63 | $(ids.push(variable!($arg));)* 64 | $crate::Expression::build_abs(1, ids, Some($body.into())) 65 | }}; 66 | } 67 | 68 | /// An application (`App`) expression 69 | #[macro_export] 70 | macro_rules! app { 71 | ($func:ident, $arg:ident) => {{ 72 | $crate::Expression::App($crate::Application( 73 | Box::new(var!($func)), 74 | Box::new(var!($arg)), 75 | )) 76 | }}; 77 | ($func:ident, $arg:expr) => {{ 78 | $crate::Expression::App($crate::Application( 79 | Box::new(var!($func)), 80 | Box::new($arg.clone().into()), 81 | )) 82 | }}; 83 | ($func:expr, $arg:ident) => {{ 84 | $crate::Expression::App($crate::Application( 85 | Box::new($func.clone().into()), 86 | Box::new(var!($arg)), 87 | )) 88 | }}; 89 | ($func:expr, $arg:expr) => {{ 90 | $crate::Expression::App($crate::Application( 91 | Box::new($func.clone().into()), 92 | Box::new($arg.clone().into()), 93 | )) 94 | }}; 95 | } 96 | 97 | /// The all-powerful λ 98 | /// 99 | /// ``` 100 | /// # #![feature(box_syntax)] 101 | /// # #[macro_use] 102 | /// # extern crate lalrpop_lambda; 103 | /// # fn main() { 104 | /// let id = λ!{x.x}; 105 | /// # } 106 | /// ``` 107 | /// 108 | /// Just a shortcut for `abs!`. 109 | #[macro_export] 110 | macro_rules! λ { 111 | {. $body:ident} => { 112 | abs!($body) 113 | }; 114 | {. $body:expr} => { 115 | abs!($body) 116 | }; 117 | {$($arg:ident)* : $ty:ident . $body:ident} => { 118 | abs!($($arg)* . $body) 119 | }; 120 | {$($arg:ident)* . $body:ident} => { 121 | abs!($($arg)* . $body) 122 | }; 123 | {$($arg:ident)* : $ty:ident . $body:expr} => {{ 124 | abs!($($arg)* . $body) 125 | }}; 126 | {$($arg:ident)* . $body:expr} => {{ 127 | abs!($($arg)* . $body) 128 | }}; 129 | } 130 | 131 | /// Theory is nothing without application 132 | /// 133 | /// This is a more terse form of `app!`. The main difference between these macros is that this 134 | /// macro wraps it's parts in `var!` expressions as needed. Whereas with `app!` we can use the 135 | /// Rust bindings to compose a new expression. Together they allow us to write: 136 | /// 137 | /// ``` 138 | /// # #![feature(box_syntax)] 139 | /// # #[macro_use] 140 | /// # extern crate lalrpop_lambda; 141 | /// # fn main() { 142 | /// let one = λ!{f.λ!{x.γ!(f, x)}}; 143 | /// let succ = λ!{n.λ!{f.λ!{x.γ!(f, γ!(n, γ!(f, x)))}}}; 144 | /// app!(succ, one); 145 | /// # } 146 | /// ``` 147 | #[macro_export] 148 | macro_rules! γ { 149 | ($func:ident, $arg:ident) => { 150 | app!(var!($func), var!($arg)) 151 | }; 152 | ($func:ident, $arg:expr) => { 153 | app!(var!($func), $arg) 154 | }; 155 | ($func:expr, $arg:ident) => { 156 | app!($func, var!($arg)) 157 | }; 158 | ($func:expr, $arg:expr) => { 159 | app!($func, $arg) 160 | }; 161 | } 162 | 163 | /// A `HashSet` macro like `map!` 164 | macro_rules! set { 165 | (@single $($x:tt)*) => (()); 166 | (@count $($rest:expr),*) => (<[()]>::len(&[$(set!(@single $rest)),*])); 167 | 168 | ($($key:expr,)+) => { 169 | set!($($key),+) 170 | }; 171 | 172 | ($($key:expr),*) => { 173 | { 174 | let _cap = set!(@count $($key),*); 175 | let mut _set = ::std::collections::HashSet::with_capacity(_cap); 176 | $( 177 | let _ = _set.insert($key); 178 | )* 179 | _set 180 | } 181 | }; 182 | } 183 | 184 | // A `HashMap` macro like `vec!` 185 | #[cfg(test)] 186 | macro_rules! map { 187 | ($($key:expr => $value:expr,)+) => { 188 | map!($($key => $value),+) 189 | }; 190 | 191 | ($($key:expr => $value:expr),*) => { 192 | { 193 | let mut _map = ::std::collections::HashMap::new(); 194 | $( 195 | let _ = _map.insert($key, $value); 196 | )* 197 | _map 198 | } 199 | }; 200 | } 201 | -------------------------------------------------------------------------------- /src/encode/boolean.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Not, BitAnd, BitOr, BitXor}; 2 | use crate::{Expression, Abstraction}; 3 | use crate::normal::Strategy; 4 | 5 | /// Church encoded booleans 6 | /// 7 | /// ``` 8 | /// # #![feature(box_syntax)] 9 | /// # #[macro_use] 10 | /// # extern crate lalrpop_lambda; 11 | /// use lalrpop_lambda::Expression; 12 | /// 13 | /// # fn main() { 14 | /// assert_eq!(λ!{a.λ!{b.a}}, Expression::from(true)); 15 | /// assert_eq!(λ!{a.λ!{b.b}}, Expression::from(false)); 16 | /// # } 17 | /// ``` 18 | impl From for Expression { 19 | fn from(p: bool) -> Self { 20 | if p { λ!{a.λ!{b.a}} } else { λ!{a.λ!{b.b}} } 21 | } 22 | } 23 | 24 | /// Convert λ term back to native Rust type 25 | /// 26 | /// ``` 27 | /// # #![feature(box_syntax)] 28 | /// # #[macro_use] 29 | /// # extern crate lalrpop_lambda; 30 | /// # fn main() { 31 | /// assert_eq!(true , bool::from(λ!{a.λ!{b.a}})); 32 | /// assert_eq!(false, bool::from(λ!{a.λ!{b.γ!(b,b)}})); 33 | /// # } 34 | /// ``` 35 | impl From for bool { 36 | fn from(e: Expression) -> bool { 37 | let s = Strategy::Applicative(true); 38 | if let Expression::Abs(Abstraction(a, box e1)) = e.normalize(&s) { 39 | if let Expression::Abs(Abstraction(_, box e2)) = e1 { 40 | if let Expression::Var(p) = e2 { 41 | return p == a 42 | } 43 | } 44 | } 45 | 46 | // Dirty hack, this should be an Option! 47 | false 48 | } 49 | } 50 | 51 | /// ``` 52 | /// # #![feature(box_syntax)] 53 | /// # #[macro_use] 54 | /// # extern crate lalrpop_lambda; 55 | /// # fn main() { 56 | /// let t = λ!{a.λ!{b.a}}; 57 | /// 58 | /// assert_eq!(false, bool::from(!t.clone())); 59 | /// assert_eq!(true, bool::from(!!t.clone())); 60 | /// # } 61 | /// ``` 62 | /// 63 | /// # Evaluation Strategy 64 | /// 65 | /// Note that there are two version of not, depending on the evaluation 66 | /// strategy that is chosen. We _currently_ only evaluate using application 67 | /// order, so we choose the first option. 68 | /// 69 | /// - Applicative evaluation order: λp.λa.λb.p b a 70 | /// - Normal evaluation order: λp.p (λa.λb.b) (λa.λb.a) 71 | impl Not for Expression { 72 | type Output = Self; 73 | 74 | fn not(self) -> Self { 75 | let not_app = λ!{p.λ!{a.λ!{b.γ!(γ!(p,b),a)}}}; 76 | γ!({not_app},{self}).normalize(&Strategy::Applicative(true)) 77 | } 78 | } 79 | 80 | /// ``` 81 | /// # #![feature(box_syntax)] 82 | /// # #[macro_use] 83 | /// # extern crate lalrpop_lambda; 84 | /// # fn main() { 85 | /// let t = λ!{a.λ!{b.a}}; 86 | /// let f = λ!{a.λ!{b.b}}; 87 | /// 88 | /// assert_eq!(true, bool::from(t.clone() | t.clone())); 89 | /// assert_eq!(true, bool::from(t.clone() | f.clone())); 90 | /// assert_eq!(true, bool::from(f.clone() | t.clone())); 91 | /// assert_eq!(false, bool::from(f.clone() | f.clone())); 92 | /// # } 93 | /// ``` 94 | impl BitOr for Expression { 95 | type Output = Self; 96 | 97 | fn bitor(self, other: Self) -> Self { 98 | let or = λ!{p.λ!{q.γ!(γ!(p,p),q)}}; 99 | γ!(γ!({or},{self}),{other}).normalize(&Strategy::Applicative(false)) 100 | } 101 | } 102 | 103 | /// ``` 104 | /// # #![feature(box_syntax)] 105 | /// # #[macro_use] 106 | /// # extern crate lalrpop_lambda; 107 | /// # fn main() { 108 | /// let t = λ!{a.λ!{b.a}}; 109 | /// let f = λ!{a.λ!{b.b}}; 110 | /// 111 | /// assert_eq!(true, bool::from(t.clone() & t.clone())); 112 | /// assert_eq!(false, bool::from(t.clone() & f.clone())); 113 | /// assert_eq!(false, bool::from(f.clone() & t.clone())); 114 | /// assert_eq!(false, bool::from(f.clone() & f.clone())); 115 | /// # } 116 | /// ``` 117 | impl BitAnd for Expression { 118 | type Output = Self; 119 | 120 | fn bitand(self, other: Self) -> Self { 121 | let and = λ!{p.λ!{q.γ!(γ!(p,q),p)}}; 122 | γ!(γ!({and},{self}),{other}).normalize(&Strategy::Applicative(false)) 123 | } 124 | } 125 | 126 | /// ``` 127 | /// # #![feature(box_syntax)] 128 | /// # #[macro_use] 129 | /// # extern crate lalrpop_lambda; 130 | /// # fn main() { 131 | /// let t = λ!{a.λ!{b.a}}; 132 | /// let f = λ!{a.λ!{b.b}}; 133 | /// 134 | /// assert_eq!(false, bool::from(t.clone() ^ t.clone())); 135 | /// assert_eq!(true, bool::from(t.clone() ^ f.clone())); 136 | /// assert_eq!(true, bool::from(f.clone() ^ t.clone())); 137 | /// assert_eq!(false, bool::from(f.clone() ^ f.clone())); 138 | /// # } 139 | /// ``` 140 | impl BitXor for Expression { 141 | type Output = Self; 142 | 143 | // TODO: This is proving we need α-renaming. 144 | fn bitxor(self, other: Self) -> Self { 145 | let not_app = λ!{p.λ!{a.λ!{b.γ!(γ!(p,b),a)}}}; 146 | let xor = λ!{p.λ!{q.γ!(γ!(p,γ!({not_app},q)),q)}}; 147 | γ!(γ!({xor},{self}),{other}).normalize(&Strategy::Applicative(false)) 148 | } 149 | } 150 | 151 | 152 | 153 | #[cfg(test)] 154 | mod tests { 155 | use pretty_assertions::assert_eq; 156 | use super::*; 157 | 158 | #[test] 159 | fn true_() { 160 | assert_eq!(true, Expression::from(true).into()); 161 | } 162 | 163 | #[test] 164 | fn false_() { 165 | assert_eq!(false, Expression::from(false).into()); 166 | } 167 | 168 | #[test] 169 | fn not() { 170 | assert_eq!(false, (!Expression::from(true)).into()); 171 | } 172 | 173 | #[test] 174 | fn or() { 175 | assert_eq!(Expression::from(true), Expression::from(true) | 176 | Expression::from(true)); 177 | assert_eq!(Expression::from(true), Expression::from(false) | 178 | Expression::from(true)); 179 | assert_eq!(Expression::from(true), Expression::from(true) | 180 | Expression::from(false)); 181 | assert_eq!(Expression::from(false), Expression::from(false) | 182 | Expression::from(false)); 183 | } 184 | 185 | #[test] 186 | fn and() { 187 | assert_eq!(Expression::from(true), Expression::from(true) & 188 | Expression::from(true)); 189 | assert_eq!(Expression::from(false), Expression::from(false) & 190 | Expression::from(true)); 191 | assert_eq!(Expression::from(false), Expression::from(true) & 192 | Expression::from(false)); 193 | assert_eq!(Expression::from(false), Expression::from(false) & 194 | Expression::from(false)); 195 | } 196 | 197 | #[test] 198 | fn xor() { 199 | assert_eq!(Expression::from(false), Expression::from(true) ^ 200 | Expression::from(true)); 201 | assert_eq!(Expression::from(true), Expression::from(false) ^ 202 | Expression::from(true)); 203 | assert_eq!(Expression::from(true), Expression::from(true) ^ 204 | Expression::from(false)); 205 | assert_eq!(Expression::from(false), Expression::from(false) ^ 206 | Expression::from(false)); 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This project started as just the parse.lalrpop and AST, but grew into a bit 2 | //! more. 3 | //! 4 | //! Evaluation of λ-expressions is _currently_ done in a single big-step 5 | //! semantics [`Expression::normalize`] function. The reduction strategy is 6 | //! so far only configurable by η. 7 | //! 8 | //! See the `impl From` items under [`Expression`]. These define conversions 9 | //! between Rust and λ-expressions. These are all defined in `mod encode`. 10 | //! 11 | //! ``` 12 | //! #![feature(box_syntax)] 13 | //! 14 | //! #[macro_use] 15 | //! extern crate lalrpop_lambda; 16 | //! 17 | //! use lalrpop_lambda::Strategy; 18 | //! 19 | //! fn main() { 20 | //! use lalrpop_lambda::Expression; 21 | //! use lalrpop_lambda::parse::ExpressionParser; 22 | //! 23 | //! // Define an expression parser, for shortest lambda term strings. 24 | //! let parser = ExpressionParser::new(); 25 | //! 26 | //! // The successor Church numeral function. 27 | //! let add1 = parser.parse("λn.λf.λx.f (n f x)").unwrap(); 28 | //! 29 | //! // The first two church numerals. 30 | //! let zero = Expression::from(0u64); 31 | //! let one = app!({add1},{zero}) 32 | //! .normalize(&Strategy::Applicative(false)); 33 | //! assert_eq!(Expression::from(1u64), one); 34 | //! 35 | //! // Use a parsed identity function with other `Experssion`s. 36 | //! let id = parser.parse("λx.x").unwrap(); 37 | //! let id_one = id(Expression::from(1u64)) 38 | //! .normalize(&Strategy::Applicative(false)); 39 | //! assert_eq!(one, id_one); 40 | //! } 41 | //! ``` 42 | #![feature(non_ascii_idents, box_patterns, fn_traits, unboxed_closures)] 43 | 44 | #[macro_use] 45 | extern crate lalrpop_util; 46 | 47 | #[cfg(feature = "wasm")] 48 | extern crate wasm_bindgen; 49 | 50 | #[cfg(test)] 51 | extern crate pretty_assertions; 52 | 53 | use std::collections::{HashMap, HashSet}; 54 | use std::fmt; 55 | 56 | #[cfg(feature = "wasm")] 57 | pub mod wasm; 58 | 59 | // TODO: Polish and test. 60 | mod normal; 61 | pub use self::normal::Strategy; 62 | 63 | // The wonderful and easy to use `λ` and `abs!` macros. 64 | // 65 | // As well as an implementation of `set!` and `map!` taken from: 66 | // [bluss/maplit](https://github.com/bluss/maplit). 67 | #[macro_use] 68 | mod macros; 69 | 70 | // Church encoded λ-calculus data types, and conversions to Rust data types 71 | mod encode; 72 | 73 | /// A mutually recursive definition for all lambda expressions 74 | /// 75 | /// ``` 76 | /// let parser = lalrpop_lambda::parse::ExpressionParser::new(); 77 | /// 78 | /// assert!(parser.parse("λx.(x x)").is_ok()); 79 | /// ``` 80 | #[derive(Clone, PartialEq, Eq)] 81 | pub enum Expression { 82 | Var(Variable), 83 | Abs(Abstraction), 84 | App(Application), 85 | } 86 | 87 | /// A potentially free variable 88 | /// 89 | /// ``` 90 | /// let parser = lalrpop_lambda::parse::ExpressionParser::new(); 91 | /// 92 | /// assert!(parser.parse("x").is_ok()); 93 | /// ``` 94 | #[derive(Clone, Hash, PartialEq, Eq)] 95 | pub struct Variable(pub String, pub Option); 96 | 97 | /// An abstraction over a bound variable 98 | /// 99 | /// ``` 100 | /// let parser = lalrpop_lambda::parse::ExpressionParser::new(); 101 | /// 102 | /// assert!(parser.parse("λx.x").is_ok()); 103 | /// ``` 104 | #[derive(Clone, PartialEq, Eq)] 105 | pub struct Abstraction(pub Variable, pub Box); 106 | 107 | /// An application of two expressions 108 | /// 109 | /// ``` 110 | /// let parser = lalrpop_lambda::parse::ExpressionParser::new(); 111 | /// 112 | /// assert!(parser.parse("a b").is_ok()); 113 | /// ``` 114 | #[derive(Clone, PartialEq, Eq)] 115 | pub struct Application(pub Box, pub Box); 116 | 117 | impl Expression { 118 | /// α-conversion 119 | pub fn rename(&self, old: &Variable, new: &Variable) -> Self { 120 | dbg!(old, new); 121 | unimplemented!() 122 | } 123 | 124 | pub fn variables(&self) -> HashSet { 125 | match self { 126 | Expression::Var(v) => set! { v.clone() }, 127 | Expression::Abs(Abstraction(id, body)) => body 128 | .variables() 129 | .union(&set! { id.clone() }) 130 | .cloned() 131 | .collect(), 132 | Expression::App(Application(e1, e2)) => { 133 | e1.variables().union(&e2.variables()).cloned().collect() 134 | } 135 | } 136 | } 137 | 138 | /// FV(M) is the set of variables in M, not closed by a λ term. 139 | /// 140 | /// ``` 141 | /// use std::collections::HashSet; 142 | /// use lalrpop_lambda::Variable; 143 | /// 144 | /// let parser = lalrpop_lambda::parse::ExpressionParser::new(); 145 | /// 146 | /// let mut free = HashSet::new(); 147 | /// free.insert(Variable("y".into(), None)); 148 | /// 149 | /// let expression = parser.parse("λx.(x y)").unwrap(); 150 | /// 151 | /// assert_eq!(free, expression.free_variables()); 152 | /// ``` 153 | pub fn free_variables(&self) -> HashSet { 154 | match self { 155 | // FV(x) = { x }, where x is a variable. 156 | Expression::Var(id) => set! { id.clone() }, 157 | // FV(λx.M) = FV(M) \ { x }. 158 | Expression::Abs(Abstraction(id, body)) => body 159 | .free_variables() 160 | .difference(&set! { id.clone() }) 161 | .cloned() 162 | .collect(), 163 | // FV(M N) = FV(M) ∪ FV(N). 164 | Expression::App(Application(e1, e2)) => e1 165 | .free_variables() 166 | .union(&e2.free_variables()) 167 | .cloned() 168 | .collect(), 169 | } 170 | } 171 | 172 | /// ``` 173 | /// # #![feature(box_syntax)] 174 | /// # #[macro_use] 175 | /// # extern crate lalrpop_lambda; 176 | /// use std::collections::HashMap; 177 | /// 178 | /// # fn main() { 179 | /// let mut env = HashMap::new(); 180 | /// env.insert(variable!(id), abs!{x.x}); 181 | /// env.insert(variable!(ad), abs!{x.y}); 182 | /// env.insert(variable!(x), 1.into()); 183 | /// 184 | /// assert_eq!(var!(q), var!(q).resolve(&env)); 185 | /// assert_eq!(1u64, var!(x).resolve(&env).into()); 186 | /// 187 | /// // Works with functions too! 188 | /// let id: fn(u64) -> u64 = var!(id).resolve(&env).into(); 189 | /// assert_eq!(1, id(1)); 190 | /// let ad: fn(u64) -> u64 = var!(ad).resolve(&env).into(); 191 | /// assert_eq!(u64::from(var!(y)), ad(0)); 192 | /// assert_eq!(u64::from(var!(y)), ad(1)); 193 | /// # } 194 | /// ``` 195 | pub fn resolve(&self, env: &HashMap) -> Expression { 196 | match self { 197 | Expression::Var(id) => { 198 | if let Some(e) = env.get(id) { 199 | e.clone() 200 | } else { 201 | self.clone() 202 | } 203 | } 204 | Expression::Abs(Abstraction(id, box body)) => { 205 | // TODO: Check FV 206 | Expression::Abs(Abstraction(id.clone(), Box::new(body.resolve(env)))) 207 | } 208 | Expression::App(Application(box e1, box e2)) => { 209 | app!({ e1.resolve(env) }, { e2.resolve(env) }) 210 | } 211 | } 212 | } 213 | } 214 | 215 | impl Expression { 216 | pub fn build_abs(lambs: usize, ids: Vec, body: Option) -> Self { 217 | // TODO: Make the body an Option too. 218 | let mut abs = body.unwrap_or(var!("")); 219 | 220 | let id_count = ids.len(); 221 | // Curry multi args. 222 | for i in ids.into_iter().rev() { 223 | abs = Expression::Abs(Abstraction(i, Box::new(abs))); 224 | } 225 | 226 | // Wrap in as many extra lambdas as requested. 227 | for l in 0..lambs { 228 | // Skip the first lambda if given any ids, since the id will have 229 | // already generated above. 230 | if l == 0 && id_count > 0 { 231 | continue; 232 | } 233 | abs = Expression::Abs(Abstraction(variable!(""), Box::new(abs))); 234 | } 235 | 236 | abs 237 | } 238 | } 239 | 240 | impl fmt::Debug for Expression { 241 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 242 | match self { 243 | Expression::Var(id) => { 244 | write!(f, "{:?}", id) 245 | } 246 | Expression::Abs(Abstraction(id, body)) => { 247 | write!(f, "(λ{:?}.{:?})", id, body) 248 | } 249 | Expression::App(Application(box e1, box e2)) => { 250 | write!(f, "({:?} {:?})", e1, e2) 251 | } 252 | } 253 | } 254 | } 255 | 256 | impl fmt::Debug for Variable { 257 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 258 | if let Some(ty) = &self.1 { 259 | write!(f, "{}:{}", self.0, ty) 260 | } else { 261 | write!(f, "{}", self.0) 262 | } 263 | } 264 | } 265 | 266 | impl fmt::Display for Expression { 267 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 268 | write!(f, "{:?}", self) 269 | } 270 | } 271 | 272 | impl fmt::Display for Variable { 273 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 274 | write!(f, "{:?}", self) 275 | } 276 | } 277 | 278 | lalrpop_mod! { 279 | /// Parse λ-expressions 280 | pub parse 281 | } 282 | 283 | #[cfg(test)] 284 | mod tests { 285 | use super::*; 286 | use crate::parse::ExpressionParser; 287 | use pretty_assertions::assert_eq; 288 | 289 | #[test] 290 | fn variable() { 291 | assert!(ExpressionParser::new().parse(r"x").is_ok()); 292 | } 293 | 294 | #[test] 295 | fn abstraction() { 296 | assert!(ExpressionParser::new().parse(r"\x.x").is_ok()); 297 | assert!(ExpressionParser::new().parse(r"\x. x").is_ok()); 298 | assert!(ExpressionParser::new().parse(r"\x.(x)").is_ok()); 299 | assert!(ExpressionParser::new().parse(r"\x. (x)").is_ok()); 300 | } 301 | 302 | #[test] 303 | fn application() { 304 | assert!(ExpressionParser::new().parse(r"x x").is_ok()); 305 | assert!(ExpressionParser::new().parse(r"(x y)").is_ok()); 306 | assert!(ExpressionParser::new().parse(r"(\x.x y)").is_ok()); 307 | } 308 | 309 | #[test] 310 | #[ignore] 311 | fn rename() {} 312 | 313 | #[test] 314 | #[ignore] 315 | fn variables() {} 316 | 317 | #[test] 318 | fn free_variables() { 319 | let parser = ExpressionParser::new(); 320 | 321 | assert_eq!( 322 | set! { variable!(x) }, 323 | parser.parse(r"x").unwrap().free_variables() 324 | ); 325 | assert_eq!(set! {}, parser.parse(r"λx.x").unwrap().free_variables()); 326 | assert_eq!( 327 | set! { variable!(f), variable!(x) }, 328 | parser.parse(r"f x").unwrap().free_variables() 329 | ); 330 | assert_eq!( 331 | set! { variable!(x), variable!(y) }, 332 | parser 333 | .parse(r"(λx.(x y)) (λy.(x y))") 334 | .unwrap() 335 | .free_variables() 336 | ); 337 | } 338 | 339 | #[test] 340 | fn resolve() { 341 | let env = map! { 342 | variable!(n) => 1.into(), 343 | }; 344 | 345 | assert_eq!(var!(q), var!(q).resolve(&env)); 346 | assert_eq!(1u64, var!(n).resolve(&env).into()); 347 | 348 | // TODO: Add more, starting with examples/env.rs. 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /src/normal.rs: -------------------------------------------------------------------------------- 1 | use crate::{Abstraction, Application, Expression, Variable}; 2 | 3 | /// A reduction strategy for an [`Expression`] 4 | pub enum Strategy { 5 | // Innermost reductions 6 | 7 | // *ao* -> normal 8 | Applicative(bool), 9 | // *bv*: not inside abstractions -> weak normal 10 | CallByValue, 11 | // ha: ao + bv -> normal 12 | HybridApplicative, 13 | 14 | // Outermost reductions 15 | 16 | // *bn*: not inside abstractions -> weak head normal 17 | CallByName, 18 | // no: bn -> normal 19 | Normal(bool), 20 | // *he*: abstractions reduced only in head position -> head normal 21 | HeadSpine(bool), 22 | // hn: no + he -> normal 23 | HybridNormal, 24 | } 25 | 26 | impl Expression { 27 | /// β-reduction small-step semantics (→) 28 | /// 29 | /// Represents local reducibility in natural deduction. 30 | /// 31 | /// - η: λx.(e1 x) -> e1 whenever x does not appear free in e1 32 | /// 33 | /// Represents local completeness in natural deduction. 34 | pub fn apply(&self, η: bool) -> Self { 35 | dbg!(η); 36 | unimplemented!() 37 | } 38 | 39 | /// Big-step natural semantics (⇓) 40 | /// 41 | /// Represents global reducibility in natural deduction. 42 | /// 43 | /// TODO: Reduction strategy. 44 | /// 45 | /// - η: (see `Expression::apply`) 46 | /// 47 | /// ``` 48 | /// use lalrpop_lambda::Strategy; 49 | /// use lalrpop_lambda::parse::ExpressionParser; 50 | /// 51 | /// let parser = ExpressionParser::new(); 52 | /// let expression = parser.parse("((λx.(λy.x y) b) a)").unwrap(); 53 | /// let normal = parser.parse("a b").unwrap(); 54 | /// 55 | /// assert_eq!(normal, expression.normalize(&Strategy::Applicative(true))); 56 | /// assert_eq!(normal, expression.normalize(&Strategy::HeadSpine(false))); 57 | /// ``` 58 | pub fn normalize(&self, strategy: &Strategy) -> Self { 59 | match *strategy { 60 | Strategy::CallByName => self.bn(), 61 | Strategy::Normal(η) => self.no(η), 62 | Strategy::CallByValue => self.bv(), 63 | Strategy::Applicative(η) => self.ao(η), 64 | Strategy::HeadSpine(η) => self.hs(η), 65 | _ => unimplemented!(), 66 | } 67 | } 68 | 69 | fn bn(&self) -> Self { 70 | match self { 71 | Expression::App(Application(box e1, box e2)) => match e1.bn() { 72 | Expression::Abs(Abstraction(id, body)) => body.substitute(&e2, &id).bn(), 73 | e @ _ => Expression::App(Application(Box::new(e), Box::new(e2.clone()))), 74 | }, 75 | _ => self.clone(), 76 | } 77 | } 78 | 79 | fn no(&self, η: bool) -> Self { 80 | match self { 81 | Expression::Var(_) => self.clone(), 82 | Expression::Abs(Abstraction(id, box body)) => { 83 | // η-reduction 84 | if let Expression::App(Application(box e1, box Expression::Var(x))) = body { 85 | if η && id == x && !e1.free_variables().contains(&id) { 86 | return e1.no(η); 87 | } 88 | } 89 | 90 | Expression::Abs(Abstraction(id.clone(), Box::new(body.no(η)))) 91 | } 92 | Expression::App(Application(box e1, box e2)) => match e1.bn() { 93 | Expression::Abs(Abstraction(id, body)) => body.substitute(&e2, &id).no(η), 94 | e @ _ => Expression::App(Application(Box::new(e.no(η)), Box::new(e2.no(η)))), 95 | }, 96 | } 97 | } 98 | 99 | fn bv(&self) -> Self { 100 | match self { 101 | Expression::App(Application(box e1, box e2)) => match e1.bv() { 102 | Expression::Abs(Abstraction(id, body)) => body.substitute(&e2.bv(), &id), 103 | e @ _ => Expression::App(Application(Box::new(e), Box::new(e2.bv()))), 104 | }, 105 | _ => self.clone(), 106 | } 107 | } 108 | 109 | fn ao(&self, η: bool) -> Self { 110 | match self { 111 | Expression::Var(_) => self.clone(), 112 | Expression::Abs(Abstraction(id, box body)) => { 113 | // η-reduction 114 | if let Expression::App(Application(box e1, box Expression::Var(x))) = body { 115 | if η && id == x && !e1.free_variables().contains(&id) { 116 | return e1.ao(η); 117 | } 118 | } 119 | 120 | Expression::Abs(Abstraction(id.clone(), Box::new(body.ao(η)))) 121 | } 122 | Expression::App(Application(box e1, box e2)) => match e1.ao(η) { 123 | Expression::Abs(Abstraction(id, body)) => body.substitute(&e2.ao(η), &id).ao(η), 124 | e @ _ => Expression::App(Application(Box::new(e), Box::new(e2.ao(η)))), 125 | }, 126 | } 127 | } 128 | 129 | fn hs(&self, η: bool) -> Self { 130 | match self { 131 | Expression::Abs(Abstraction(id, box body)) => { 132 | // η-reduction 133 | if let Expression::App(Application(box e1, box Expression::Var(x))) = body { 134 | if η && id == x && !e1.free_variables().contains(&id) { 135 | return e1.hs(η); 136 | } 137 | } 138 | 139 | Expression::Abs(Abstraction(id.clone(), Box::new(body.hs(η)))) 140 | } 141 | Expression::App(Application(box e1, box e2)) => match e1.bn() { 142 | Expression::Abs(Abstraction(id, body)) => body.substitute(&e2, &id), 143 | e @ _ => Expression::App(Application(Box::new(e), Box::new(e2.clone()))), 144 | }, 145 | _ => self.clone(), 146 | } 147 | } 148 | 149 | /// self[x := v] 150 | fn substitute(&self, v: &Self, x: &Variable) -> Self { 151 | match self { 152 | Expression::Abs(Abstraction(id, box body)) => { 153 | if id == x || !v.free_variables().contains(id) { 154 | Expression::Abs(Abstraction(id.clone(), Box::new(body.substitute(v, x)))) 155 | } else { 156 | let fresh = Variable(format!("{}'", id), None); 157 | let body = body.replace(&id, &fresh); 158 | Expression::Abs(Abstraction(fresh, Box::new(body.substitute(v, x)))) 159 | } 160 | } 161 | Expression::Var(id) => (if id == x { v } else { self }).clone(), 162 | Expression::App(Application(e1, e2)) => Expression::App(Application( 163 | Box::new(e1.substitute(v, x)), 164 | Box::new(e2.substitute(v, x)), 165 | )), 166 | } 167 | } 168 | 169 | fn replace(&self, old: &Variable, new: &Variable) -> Self { 170 | match self { 171 | Expression::Var(v) => Expression::Var(v.replace(old, new)), 172 | Expression::Abs(Abstraction(id, body)) => Expression::Abs(Abstraction( 173 | id.replace(old, new), 174 | Box::new(body.replace(old, new)), 175 | )), 176 | Expression::App(Application(e1, e2)) => Expression::App(Application( 177 | Box::new(e1.replace(old, new)), 178 | Box::new(e2.replace(old, new)), 179 | )), 180 | } 181 | } 182 | } 183 | 184 | impl Variable { 185 | fn replace(&self, old: &Variable, new: &Variable) -> Self { 186 | if self.0 == old.0 { 187 | Variable(new.0.clone(), new.1.clone()) 188 | } else { 189 | self.clone() 190 | } 191 | } 192 | } 193 | 194 | #[cfg(test)] 195 | mod tests { 196 | use super::*; 197 | use crate::{abs, app, var, variable}; 198 | use pretty_assertions::assert_eq; 199 | 200 | #[test] 201 | #[ignore] 202 | fn apply() {} 203 | 204 | #[test] 205 | fn normalize() { 206 | let strategy = Strategy::Applicative(false); 207 | 208 | assert_eq!(var!(a), app!(abs! {x.x}, a).normalize(&strategy)); 209 | 210 | assert_eq!( 211 | app!(a, a), 212 | app!(abs! {x.app!(abs!{x.app!(x, x)}, a)}, b).normalize(&strategy) 213 | ); 214 | assert_eq!( 215 | app!(a, b), 216 | app!(abs! {y.app!(a, y)}, b).normalize(&strategy) 217 | ); 218 | assert_eq!( 219 | app!(b, a), 220 | app!(app!(abs! {x.abs!{y.app!(x, y)}}, b), a).normalize(&strategy) 221 | ); 222 | assert_eq!( 223 | app!(b, b), 224 | app!(app!(abs! {x.abs!{y.app!(x, y)}}, b), b).normalize(&strategy) 225 | ); 226 | 227 | assert_eq!( 228 | abs! {a.a}, 229 | app!(abs! {x.x}, abs! {a.a}).normalize(&strategy) 230 | ); 231 | println!("{}", app!(abs! {f.abs!{x.app!(f,a)}}, abs! {x.x})); 232 | assert_eq!( 233 | abs! {x.a}, 234 | app!(abs! {f.abs!{x.app!(f,a)}}, abs! {x.x}).normalize(&strategy) 235 | ); 236 | } 237 | 238 | #[test] 239 | fn normalize_capture_avoid() { 240 | let strategy = Strategy::Applicative(false); 241 | 242 | let expected = Expression::Abs(Abstraction( 243 | variable!("y"), 244 | Box::new(Expression::Abs(Abstraction( 245 | variable!("y'"), 246 | Box::new(Expression::Var(variable!("y"))), 247 | ))), 248 | )); 249 | let actual = abs! {y.app!(abs!{x.abs!{y.x}}, y)}; 250 | assert_eq!(expected, actual.normalize(&strategy)); 251 | 252 | let expected = abs! {f.abs!{x.app!(var!(f), 253 | Expression::Abs(Abstraction(variable!("x'"), 254 | Box::new(app!(app!(var!(f), 255 | var!(x)), 256 | var!("x'"))))))}}; 257 | let actual = app!( 258 | abs! {n.abs!{f.abs!{x.app!(f, app!(n, app!(f, x)))}}}, 259 | abs! {f.abs!{x.app!(f, x)}} 260 | ); 261 | assert_eq!(expected, actual.normalize(&strategy)); 262 | 263 | let expected = Expression::Abs(Abstraction(variable!("x'"), Box::new(var!(x)))); 264 | let actual = app!(abs! {f.abs!{x.app!(f,a)}}, abs! {a.x}).normalize(&strategy); 265 | assert_eq!(expected, actual); 266 | 267 | let expected = abs! {f.abs!{x.app!(f,{ 268 | let x2 = Expression::Abs(Abstraction(variable!("x''"), 269 | Box::new(var!("x''")))); 270 | let fx = app!(app!(f,x),{x2}); 271 | Expression::Abs(Abstraction(variable!("x'"), 272 | Box::new(fx))) 273 | })}}; 274 | let actual = app!( 275 | abs! {n.abs!{f.abs!{x.app!(f,app!(n,app!(f,x)))}}}, 276 | app!( 277 | abs! {n.abs!{f.abs!{x.app!(f,app!(n,app!(f,x)))}}}, 278 | abs! {f.abs!{x.x}} 279 | ) 280 | ); 281 | assert_eq!(expected, actual.normalize(&strategy)); 282 | } 283 | 284 | #[test] 285 | fn normalize_η() { 286 | let strategy = Strategy::Applicative(true); 287 | 288 | assert_eq!(var!(f), abs! {x.app!(f,x)}.normalize(&strategy)); 289 | assert_eq!(abs! {x.app!(x,x)}, abs! {x.app!(x, x)}.normalize(&strategy)); 290 | assert_eq!(abs! {f.f}, abs! {f.abs!{g.abs!{x.app!(app!(f,g),x)}}}); 291 | } 292 | 293 | #[test] 294 | #[ignore] 295 | #[allow(non_snake_case)] 296 | fn normalize_Ω() { 297 | let strategy = Strategy::Applicative(false); 298 | 299 | let Ω = app!(abs! {x.app!(x,x)}, abs! {x.app!(x,x)}); 300 | assert_eq!(abs! {x.x}, Ω.normalize(&strategy)); 301 | } 302 | 303 | // TODO: Strategy testing. 304 | 305 | #[test] 306 | fn replace() { 307 | assert_eq!(var!(b), var!(a).replace(&variable!(a), &variable!(b))); 308 | assert_eq!(app!(b, b), app!(a, a).replace(&variable!(a), &variable!(b))); 309 | assert_eq!(abs! {b.b}, abs! {a.a}.replace(&variable!(a), &variable!(b))); 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /benches/baselines/v0.3.0_bench.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base", 3 | "benchmarks": { 4 | "native addition/0": { 5 | "baseline": "base", 6 | "fullname": "base/native addition/0", 7 | "criterion_benchmark_v1": { 8 | "group_id": "native addition", 9 | "function_id": null, 10 | "value_str": "0", 11 | "throughput": null, 12 | "full_id": "native addition/0", 13 | "directory_name": "native addition/0" 14 | }, 15 | "criterion_estimates_v1": { 16 | "Mean": { 17 | "confidence_interval": { 18 | "confidence_level": 0.95, 19 | "lower_bound": 0.2968468484936433, 20 | "upper_bound": 0.29981451390539515 21 | }, 22 | "point_estimate": 0.2980689101273462, 23 | "standard_error": 0.0007732078076600166 24 | }, 25 | "Median": { 26 | "confidence_interval": { 27 | "confidence_level": 0.95, 28 | "lower_bound": 0.2963090448367719, 29 | "upper_bound": 0.29644436136993063 30 | }, 31 | "point_estimate": 0.29636802229825265, 32 | "standard_error": 0.000035282650295655695 33 | }, 34 | "MedianAbsDev": { 35 | "confidence_interval": { 36 | "confidence_level": 0.95, 37 | "lower_bound": 0.0002254411781712316, 38 | "upper_bound": 0.0004287219866189192 39 | }, 40 | "point_estimate": 0.00030633345954119817, 41 | "standard_error": 0.00005164797710510594 42 | }, 43 | "Slope": { 44 | "confidence_interval": { 45 | "confidence_level": 0.95, 46 | "lower_bound": 0.29671960655889934, 47 | "upper_bound": 0.30091325223318677 48 | }, 49 | "point_estimate": 0.2983809343621838, 50 | "standard_error": 0.001112998300797706 51 | }, 52 | "StdDev": { 53 | "confidence_interval": { 54 | "confidence_level": 0.95, 55 | "lower_bound": 0.0019952290708290304, 56 | "upper_bound": 0.012467394399385635 57 | }, 58 | "point_estimate": 0.007780645055749648, 59 | "standard_error": 0.0029524566947364372 60 | } 61 | } 62 | }, 63 | "native addition/1": { 64 | "baseline": "base", 65 | "fullname": "base/native addition/1", 66 | "criterion_benchmark_v1": { 67 | "group_id": "native addition", 68 | "function_id": null, 69 | "value_str": "1", 70 | "throughput": null, 71 | "full_id": "native addition/1", 72 | "directory_name": "native addition/1" 73 | }, 74 | "criterion_estimates_v1": { 75 | "Mean": { 76 | "confidence_interval": { 77 | "confidence_level": 0.95, 78 | "lower_bound": 0.2974932569243279, 79 | "upper_bound": 0.30142455551001746 80 | }, 81 | "point_estimate": 0.29916448791189754, 82 | "standard_error": 0.001018035683551917 83 | }, 84 | "Median": { 85 | "confidence_interval": { 86 | "confidence_level": 0.95, 87 | "lower_bound": 0.2966614004043141, 88 | "upper_bound": 0.29684766843879223 89 | }, 90 | "point_estimate": 0.29674107319979465, 91 | "standard_error": 0.00005100175563118712 92 | }, 93 | "MedianAbsDev": { 94 | "confidence_interval": { 95 | "confidence_level": 0.95, 96 | "lower_bound": 0.0002549709505649339, 97 | "upper_bound": 0.0005400208952110087 98 | }, 99 | "point_estimate": 0.0004181377497412315, 100 | "standard_error": 0.00007269021666376222 101 | }, 102 | "Slope": { 103 | "confidence_interval": { 104 | "confidence_level": 0.95, 105 | "lower_bound": 0.2971608543346816, 106 | "upper_bound": 0.3002425134055753 107 | }, 108 | "point_estimate": 0.2984892274903682, 109 | "standard_error": 0.0007897881148983396 110 | }, 111 | "StdDev": { 112 | "confidence_interval": { 113 | "confidence_level": 0.95, 114 | "lower_bound": 0.003405162674502418, 115 | "upper_bound": 0.015792138091545085 116 | }, 117 | "point_estimate": 0.01021602855732574, 118 | "standard_error": 0.0032816176989411577 119 | } 120 | } 121 | }, 122 | "native addition/16": { 123 | "baseline": "base", 124 | "fullname": "base/native addition/16", 125 | "criterion_benchmark_v1": { 126 | "group_id": "native addition", 127 | "function_id": null, 128 | "value_str": "16", 129 | "throughput": null, 130 | "full_id": "native addition/16", 131 | "directory_name": "native addition/16" 132 | }, 133 | "criterion_estimates_v1": { 134 | "Mean": { 135 | "confidence_interval": { 136 | "confidence_level": 0.95, 137 | "lower_bound": 0.29703259415180594, 138 | "upper_bound": 0.30458481999513154 139 | }, 140 | "point_estimate": 0.2999152911576112, 141 | "standard_error": 0.0020255055362622867 142 | }, 143 | "Median": { 144 | "confidence_interval": { 145 | "confidence_level": 0.95, 146 | "lower_bound": 0.296028293280678, 147 | "upper_bound": 0.2962186846897828 148 | }, 149 | "point_estimate": 0.29609128878637136, 150 | "standard_error": 0.00004743259148512791 151 | }, 152 | "MedianAbsDev": { 153 | "confidence_interval": { 154 | "confidence_level": 0.95, 155 | "lower_bound": 0.00008149309004046841, 156 | "upper_bound": 0.00034947330618364224 157 | }, 158 | "point_estimate": 0.0001685614645881633, 159 | "standard_error": 0.0000674481797812517 160 | }, 161 | "Slope": { 162 | "confidence_interval": { 163 | "confidence_level": 0.95, 164 | "lower_bound": 0.296694330189109, 165 | "upper_bound": 0.29972462396528426 166 | }, 167 | "point_estimate": 0.2980324283787414, 168 | "standard_error": 0.0007772907743877836 169 | }, 170 | "StdDev": { 171 | "confidence_interval": { 172 | "confidence_level": 0.95, 173 | "lower_bound": 0.0035639092472373513, 174 | "upper_bound": 0.03375769020935149 175 | }, 176 | "point_estimate": 0.020319324150107627, 177 | "standard_error": 0.009467540875262671 178 | } 179 | } 180 | }, 181 | "native addition/2": { 182 | "baseline": "base", 183 | "fullname": "base/native addition/2", 184 | "criterion_benchmark_v1": { 185 | "group_id": "native addition", 186 | "function_id": null, 187 | "value_str": "2", 188 | "throughput": null, 189 | "full_id": "native addition/2", 190 | "directory_name": "native addition/2" 191 | }, 192 | "criterion_estimates_v1": { 193 | "Mean": { 194 | "confidence_interval": { 195 | "confidence_level": 0.95, 196 | "lower_bound": 0.2970807937744729, 197 | "upper_bound": 0.3003533505568965 198 | }, 199 | "point_estimate": 0.29847613972340964, 200 | "standard_error": 0.0008471106951294426 201 | }, 202 | "Median": { 203 | "confidence_interval": { 204 | "confidence_level": 0.95, 205 | "lower_bound": 0.2962759008983252, 206 | "upper_bound": 0.29686478522322357 207 | }, 208 | "point_estimate": 0.29654257081374513, 209 | "standard_error": 0.0001430952117779159 210 | }, 211 | "MedianAbsDev": { 212 | "confidence_interval": { 213 | "confidence_level": 0.95, 214 | "lower_bound": 0.00042556487685459384, 215 | "upper_bound": 0.0010140397355954752 216 | }, 217 | "point_estimate": 0.0007030435548050374, 218 | "standard_error": 0.00014045004917796367 219 | }, 220 | "Slope": { 221 | "confidence_interval": { 222 | "confidence_level": 0.95, 223 | "lower_bound": 0.2972728090254777, 224 | "upper_bound": 0.3030484171817896 225 | }, 226 | "point_estimate": 0.2994483113779417, 227 | "standard_error": 0.0015623364896958836 228 | }, 229 | "StdDev": { 230 | "confidence_interval": { 231 | "confidence_level": 0.95, 232 | "lower_bound": 0.0022022166404387527, 233 | "upper_bound": 0.012921950064696168 234 | }, 235 | "point_estimate": 0.008518972608538606, 236 | "standard_error": 0.0028147965992940107 237 | } 238 | } 239 | }, 240 | "native addition/32": { 241 | "baseline": "base", 242 | "fullname": "base/native addition/32", 243 | "criterion_benchmark_v1": { 244 | "group_id": "native addition", 245 | "function_id": null, 246 | "value_str": "32", 247 | "throughput": null, 248 | "full_id": "native addition/32", 249 | "directory_name": "native addition/32" 250 | }, 251 | "criterion_estimates_v1": { 252 | "Mean": { 253 | "confidence_interval": { 254 | "confidence_level": 0.95, 255 | "lower_bound": 0.297275827552392, 256 | "upper_bound": 0.30013184095846446 257 | }, 258 | "point_estimate": 0.29851096988470344, 259 | "standard_error": 0.0007397907661081399 260 | }, 261 | "Median": { 262 | "confidence_interval": { 263 | "confidence_level": 0.95, 264 | "lower_bound": 0.2966867798893299, 265 | "upper_bound": 0.2969044243893952 266 | }, 267 | "point_estimate": 0.2967735926061519, 268 | "standard_error": 0.0000570089318612994 269 | }, 270 | "MedianAbsDev": { 271 | "confidence_interval": { 272 | "confidence_level": 0.95, 273 | "lower_bound": 0.0003309350474998074, 274 | "upper_bound": 0.0006248792135043663 275 | }, 276 | "point_estimate": 0.0004845653264040483, 277 | "standard_error": 0.0000780768625908119 278 | }, 279 | "Slope": { 280 | "confidence_interval": { 281 | "confidence_level": 0.95, 282 | "lower_bound": 0.2971032978333613, 283 | "upper_bound": 0.3025042677653186 284 | }, 285 | "point_estimate": 0.29932444397541724, 286 | "standard_error": 0.0014132132256441975 287 | }, 288 | "StdDev": { 289 | "confidence_interval": { 290 | "confidence_level": 0.95, 291 | "lower_bound": 0.0027480440668540716, 292 | "upper_bound": 0.011290301535211246 293 | }, 294 | "point_estimate": 0.007447559573931257, 295 | "standard_error": 0.002232602955317096 296 | } 297 | } 298 | }, 299 | "native addition/4": { 300 | "baseline": "base", 301 | "fullname": "base/native addition/4", 302 | "criterion_benchmark_v1": { 303 | "group_id": "native addition", 304 | "function_id": null, 305 | "value_str": "4", 306 | "throughput": null, 307 | "full_id": "native addition/4", 308 | "directory_name": "native addition/4" 309 | }, 310 | "criterion_estimates_v1": { 311 | "Mean": { 312 | "confidence_interval": { 313 | "confidence_level": 0.95, 314 | "lower_bound": 0.2966483633133743, 315 | "upper_bound": 0.2999014682361953 316 | }, 317 | "point_estimate": 0.2980454326978392, 318 | "standard_error": 0.000841302596453012 319 | }, 320 | "Median": { 321 | "confidence_interval": { 322 | "confidence_level": 0.95, 323 | "lower_bound": 0.2960522229086636, 324 | "upper_bound": 0.2961609506703249 325 | }, 326 | "point_estimate": 0.29609173697738067, 327 | "standard_error": 0.00002747845366577081 328 | }, 329 | "MedianAbsDev": { 330 | "confidence_interval": { 331 | "confidence_level": 0.95, 332 | "lower_bound": 0.00010453914475976706, 333 | "upper_bound": 0.0002269633664783 334 | }, 335 | "point_estimate": 0.00016010171203138603, 336 | "standard_error": 0.00003019504692412464 337 | }, 338 | "Slope": { 339 | "confidence_interval": { 340 | "confidence_level": 0.95, 341 | "lower_bound": 0.29652830364262145, 342 | "upper_bound": 0.30120607442341485 343 | }, 344 | "point_estimate": 0.2983130531577798, 345 | "standard_error": 0.001252348934888083 346 | }, 347 | "StdDev": { 348 | "confidence_interval": { 349 | "confidence_level": 0.95, 350 | "lower_bound": 0.0021139999809323, 351 | "upper_bound": 0.012823699698046956 352 | }, 353 | "point_estimate": 0.008445058695073207, 354 | "standard_error": 0.0027015276518215373 355 | } 356 | } 357 | }, 358 | "native addition/8": { 359 | "baseline": "base", 360 | "fullname": "base/native addition/8", 361 | "criterion_benchmark_v1": { 362 | "group_id": "native addition", 363 | "function_id": null, 364 | "value_str": "8", 365 | "throughput": null, 366 | "full_id": "native addition/8", 367 | "directory_name": "native addition/8" 368 | }, 369 | "criterion_estimates_v1": { 370 | "Mean": { 371 | "confidence_interval": { 372 | "confidence_level": 0.95, 373 | "lower_bound": 0.2966275574721007, 374 | "upper_bound": 0.3004758779076329 375 | }, 376 | "point_estimate": 0.2981698109443507, 377 | "standard_error": 0.001013961769455673 378 | }, 379 | "Median": { 380 | "confidence_interval": { 381 | "confidence_level": 0.95, 382 | "lower_bound": 0.2960783413290481, 383 | "upper_bound": 0.29615919202533053 384 | }, 385 | "point_estimate": 0.296108471868778, 386 | "standard_error": 0.00002065821411138976 387 | }, 388 | "MedianAbsDev": { 389 | "confidence_interval": { 390 | "confidence_level": 0.95, 391 | "lower_bound": 0.00010137151395098546, 392 | "upper_bound": 0.0001996008745075391 393 | }, 394 | "point_estimate": 0.0001438057706566066, 395 | "standard_error": 0.000026051713001064855 396 | }, 397 | "Slope": { 398 | "confidence_interval": { 399 | "confidence_level": 0.95, 400 | "lower_bound": 0.2966422481588065, 401 | "upper_bound": 0.30105135041024694 402 | }, 403 | "point_estimate": 0.2985043341611148, 404 | "standard_error": 0.001145339020403624 405 | }, 406 | "StdDev": { 407 | "confidence_interval": { 408 | "confidence_level": 0.95, 409 | "lower_bound": 0.002346781134368851, 410 | "upper_bound": 0.016538811345455227 411 | }, 412 | "point_estimate": 0.010162159068917148, 413 | "standard_error": 0.00418112059410067 414 | } 415 | } 416 | }, 417 | "λ-expression addition/0": { 418 | "baseline": "base", 419 | "fullname": "base/λ-expression addition/0", 420 | "criterion_benchmark_v1": { 421 | "group_id": "λ-expression addition", 422 | "function_id": null, 423 | "value_str": "0", 424 | "throughput": null, 425 | "full_id": "λ-expression addition/0", 426 | "directory_name": "λ-expression addition/0" 427 | }, 428 | "criterion_estimates_v1": { 429 | "Mean": { 430 | "confidence_interval": { 431 | "confidence_level": 0.95, 432 | "lower_bound": 13449.443388550942, 433 | "upper_bound": 13555.744922434284 434 | }, 435 | "point_estimate": 13496.275874948264, 436 | "standard_error": 27.35214778416823 437 | }, 438 | "Median": { 439 | "confidence_interval": { 440 | "confidence_level": 0.95, 441 | "lower_bound": 13406.31446414182, 442 | "upper_bound": 13430.824033272664 443 | }, 444 | "point_estimate": 13421.207743408457, 445 | "standard_error": 5.913463768724566 446 | }, 447 | "MedianAbsDev": { 448 | "confidence_interval": { 449 | "confidence_level": 0.95, 450 | "lower_bound": 23.306377088076676, 451 | "upper_bound": 45.40485672779425 452 | }, 453 | "point_estimate": 36.32231699279966, 454 | "standard_error": 5.775624284504741 455 | }, 456 | "Slope": { 457 | "confidence_interval": { 458 | "confidence_level": 0.95, 459 | "lower_bound": 13438.572144861288, 460 | "upper_bound": 13628.663197279877 461 | }, 462 | "point_estimate": 13515.054902862605, 463 | "standard_error": 50.28906597420155 464 | }, 465 | "StdDev": { 466 | "confidence_interval": { 467 | "confidence_level": 0.95, 468 | "lower_bound": 129.4971209105263, 469 | "upper_bound": 408.47714957406214 470 | }, 471 | "point_estimate": 274.74619908142836, 472 | "standard_error": 75.60252417182761 473 | } 474 | } 475 | }, 476 | "λ-expression addition/1": { 477 | "baseline": "base", 478 | "fullname": "base/λ-expression addition/1", 479 | "criterion_benchmark_v1": { 480 | "group_id": "λ-expression addition", 481 | "function_id": null, 482 | "value_str": "1", 483 | "throughput": null, 484 | "full_id": "λ-expression addition/1", 485 | "directory_name": "λ-expression addition/1" 486 | }, 487 | "criterion_estimates_v1": { 488 | "Mean": { 489 | "confidence_interval": { 490 | "confidence_level": 0.95, 491 | "lower_bound": 22032.35135384203, 492 | "upper_bound": 22311.331190225337 493 | }, 494 | "point_estimate": 22154.54140340484, 495 | "standard_error": 71.68197757581329 496 | }, 497 | "Median": { 498 | "confidence_interval": { 499 | "confidence_level": 0.95, 500 | "lower_bound": 21963.818055555555, 501 | "upper_bound": 21971.856307258633 502 | }, 503 | "point_estimate": 21967.231196581197, 504 | "standard_error": 1.9589172227392468 505 | }, 506 | "MedianAbsDev": { 507 | "confidence_interval": { 508 | "confidence_level": 0.95, 509 | "lower_bound": 11.18555914508616, 510 | "upper_bound": 19.52331038509972 511 | }, 512 | "point_estimate": 16.061021706890156, 513 | "standard_error": 2.088810251537709 514 | }, 515 | "Slope": { 516 | "confidence_interval": { 517 | "confidence_level": 0.95, 518 | "lower_bound": 21989.711499063185, 519 | "upper_bound": 22272.396654948556 520 | }, 521 | "point_estimate": 22104.353070620495, 522 | "standard_error": 74.1513907698145 523 | }, 524 | "StdDev": { 525 | "confidence_interval": { 526 | "confidence_level": 0.95, 527 | "lower_bound": 290.66482415234725, 528 | "upper_bound": 1048.4154706111842 529 | }, 530 | "point_estimate": 719.2075070528622, 531 | "standard_error": 192.42737433454823 532 | } 533 | } 534 | }, 535 | "λ-expression addition/16": { 536 | "baseline": "base", 537 | "fullname": "base/λ-expression addition/16", 538 | "criterion_benchmark_v1": { 539 | "group_id": "λ-expression addition", 540 | "function_id": null, 541 | "value_str": "16", 542 | "throughput": null, 543 | "full_id": "λ-expression addition/16", 544 | "directory_name": "λ-expression addition/16" 545 | }, 546 | "criterion_estimates_v1": { 547 | "Mean": { 548 | "confidence_interval": { 549 | "confidence_level": 0.95, 550 | "lower_bound": 412688.4729891293, 551 | "upper_bound": 417838.82914217864 552 | }, 553 | "point_estimate": 415026.3712592694, 554 | "standard_error": 1321.450686255857 555 | }, 556 | "Median": { 557 | "confidence_interval": { 558 | "confidence_level": 0.95, 559 | "lower_bound": 410607.7466094033, 560 | "upper_bound": 410832.15012368583 561 | }, 562 | "point_estimate": 410650.9106060606, 563 | "standard_error": 59.9685133007851 564 | }, 565 | "MedianAbsDev": { 566 | "confidence_interval": { 567 | "confidence_level": 0.95, 568 | "lower_bound": 223.9008251745031, 569 | "upper_bound": 496.77364758318936 570 | }, 571 | "point_estimate": 311.86784201566525, 572 | "standard_error": 66.72033379729751 573 | }, 574 | "Slope": { 575 | "confidence_interval": { 576 | "confidence_level": 0.95, 577 | "lower_bound": 411674.5508292543, 578 | "upper_bound": 415239.8957054938 579 | }, 580 | "point_estimate": 413285.14755923353, 581 | "standard_error": 914.4441412365476 582 | }, 583 | "StdDev": { 584 | "confidence_interval": { 585 | "confidence_level": 0.95, 586 | "lower_bound": 7488.577039016398, 587 | "upper_bound": 17910.407834841233 588 | }, 589 | "point_estimate": 13283.1709721034, 590 | "standard_error": 2652.6960868847145 591 | } 592 | } 593 | }, 594 | "λ-expression addition/2": { 595 | "baseline": "base", 596 | "fullname": "base/λ-expression addition/2", 597 | "criterion_benchmark_v1": { 598 | "group_id": "λ-expression addition", 599 | "function_id": null, 600 | "value_str": "2", 601 | "throughput": null, 602 | "full_id": "λ-expression addition/2", 603 | "directory_name": "λ-expression addition/2" 604 | }, 605 | "criterion_estimates_v1": { 606 | "Mean": { 607 | "confidence_interval": { 608 | "confidence_level": 0.95, 609 | "lower_bound": 32861.60347195717, 610 | "upper_bound": 33390.167925269394 611 | }, 612 | "point_estimate": 33093.77925681181, 613 | "standard_error": 136.11088228956922 614 | }, 615 | "Median": { 616 | "confidence_interval": { 617 | "confidence_level": 0.95, 618 | "lower_bound": 32684.714403600898, 619 | "upper_bound": 32736.470430107525 620 | }, 621 | "point_estimate": 32719.647099192793, 622 | "standard_error": 13.91090300183185 623 | }, 624 | "MedianAbsDev": { 625 | "confidence_interval": { 626 | "confidence_level": 0.95, 627 | "lower_bound": 78.35166225414126, 628 | "upper_bound": 144.98949330267604 629 | }, 630 | "point_estimate": 112.85152322176866, 631 | "standard_error": 16.868616388083712 632 | }, 633 | "Slope": { 634 | "confidence_interval": { 635 | "confidence_level": 0.95, 636 | "lower_bound": 32773.84658960515, 637 | "upper_bound": 33241.935849819274 638 | }, 639 | "point_estimate": 32972.814606081694, 640 | "standard_error": 121.561459400322 641 | }, 642 | "StdDev": { 643 | "confidence_interval": { 644 | "confidence_level": 0.95, 645 | "lower_bound": 541.0552766722783, 646 | "upper_bound": 1990.8429086743072 647 | }, 648 | "point_estimate": 1369.0084500725852, 649 | "standard_error": 373.46146252476063 650 | } 651 | } 652 | }, 653 | "λ-expression addition/32": { 654 | "baseline": "base", 655 | "fullname": "base/λ-expression addition/32", 656 | "criterion_benchmark_v1": { 657 | "group_id": "λ-expression addition", 658 | "function_id": null, 659 | "value_str": "32", 660 | "throughput": null, 661 | "full_id": "λ-expression addition/32", 662 | "directory_name": "λ-expression addition/32" 663 | }, 664 | "criterion_estimates_v1": { 665 | "Mean": { 666 | "confidence_interval": { 667 | "confidence_level": 0.95, 668 | "lower_bound": 1408541.2890109746, 669 | "upper_bound": 1422880.1625501968 670 | }, 671 | "point_estimate": 1415160.637315932, 672 | "standard_error": 3673.979222997814 673 | }, 674 | "Median": { 675 | "confidence_interval": { 676 | "confidence_level": 0.95, 677 | "lower_bound": 1402206.066666667, 678 | "upper_bound": 1403709.4047619049 679 | }, 680 | "point_estimate": 1402794.5511904764, 681 | "standard_error": 356.30215858853774 682 | }, 683 | "MedianAbsDev": { 684 | "confidence_interval": { 685 | "confidence_level": 0.95, 686 | "lower_bound": 2636.1554144026177, 687 | "upper_bound": 5321.216823364671 688 | }, 689 | "point_estimate": 4100.105962760796, 690 | "standard_error": 701.8382113562593 691 | }, 692 | "Slope": { 693 | "confidence_interval": { 694 | "confidence_level": 0.95, 695 | "lower_bound": 1406443.225470772, 696 | "upper_bound": 1420864.0650071786 697 | }, 698 | "point_estimate": 1412637.77345648, 699 | "standard_error": 3760.796398785651 700 | }, 701 | "StdDev": { 702 | "confidence_interval": { 703 | "confidence_level": 0.95, 704 | "lower_bound": 23902.71680683939, 705 | "upper_bound": 47322.099934590835 706 | }, 707 | "point_estimate": 36936.647888004954, 708 | "standard_error": 5997.233896050246 709 | } 710 | } 711 | }, 712 | "λ-expression addition/4": { 713 | "baseline": "base", 714 | "fullname": "base/λ-expression addition/4", 715 | "criterion_benchmark_v1": { 716 | "group_id": "λ-expression addition", 717 | "function_id": null, 718 | "value_str": "4", 719 | "throughput": null, 720 | "full_id": "λ-expression addition/4", 721 | "directory_name": "λ-expression addition/4" 722 | }, 723 | "criterion_estimates_v1": { 724 | "Mean": { 725 | "confidence_interval": { 726 | "confidence_level": 0.95, 727 | "lower_bound": 61153.60752645869, 728 | "upper_bound": 62527.283763870786 729 | }, 730 | "point_estimate": 61773.95857713562, 731 | "standard_error": 352.3513592188564 732 | }, 733 | "Median": { 734 | "confidence_interval": { 735 | "confidence_level": 0.95, 736 | "lower_bound": 60601.502521008406, 737 | "upper_bound": 60642.39956958393 738 | }, 739 | "point_estimate": 60625.619453044375, 740 | "standard_error": 10.667326522819192 741 | }, 742 | "MedianAbsDev": { 743 | "confidence_interval": { 744 | "confidence_level": 0.95, 745 | "lower_bound": 65.37024793447279, 746 | "upper_bound": 152.5597123901359 747 | }, 748 | "point_estimate": 96.93416983974528, 749 | "standard_error": 20.848799467836837 750 | }, 751 | "Slope": { 752 | "confidence_interval": { 753 | "confidence_level": 0.95, 754 | "lower_bound": 60871.17685548583, 755 | "upper_bound": 61981.24194778028 756 | }, 757 | "point_estimate": 61333.74746129574, 758 | "standard_error": 287.4252267957783 759 | }, 760 | "StdDev": { 761 | "confidence_interval": { 762 | "confidence_level": 0.95, 763 | "lower_bound": 1776.121808989942, 764 | "upper_bound": 4904.638476474027 765 | }, 766 | "point_estimate": 3542.5899519992336, 767 | "standard_error": 791.2978950575418 768 | } 769 | } 770 | }, 771 | "λ-expression addition/8": { 772 | "baseline": "base", 773 | "fullname": "base/λ-expression addition/8", 774 | "criterion_benchmark_v1": { 775 | "group_id": "λ-expression addition", 776 | "function_id": null, 777 | "value_str": "8", 778 | "throughput": null, 779 | "full_id": "λ-expression addition/8", 780 | "directory_name": "λ-expression addition/8" 781 | }, 782 | "criterion_estimates_v1": { 783 | "Mean": { 784 | "confidence_interval": { 785 | "confidence_level": 0.95, 786 | "lower_bound": 142336.4996254305, 787 | "upper_bound": 144030.23561268055 788 | }, 789 | "point_estimate": 143106.76805599275, 790 | "standard_error": 433.72120854000894 791 | }, 792 | "Median": { 793 | "confidence_interval": { 794 | "confidence_level": 0.95, 795 | "lower_bound": 141762.5974851788, 796 | "upper_bound": 141851.92261904763 797 | }, 798 | "point_estimate": 141794.19977324264, 799 | "standard_error": 25.28157656489059 800 | }, 801 | "MedianAbsDev": { 802 | "confidence_interval": { 803 | "confidence_level": 0.95, 804 | "lower_bound": 106.4318448146757, 805 | "upper_bound": 200.434860462588 806 | }, 807 | "point_estimate": 134.9374721235973, 808 | "standard_error": 25.03407228543716 809 | }, 810 | "Slope": { 811 | "confidence_interval": { 812 | "confidence_level": 0.95, 813 | "lower_bound": 142008.04124203886, 814 | "upper_bound": 143368.54007868798 815 | }, 816 | "point_estimate": 142612.91705714707, 817 | "standard_error": 348.563886654758 818 | }, 819 | "StdDev": { 820 | "confidence_interval": { 821 | "confidence_level": 0.95, 822 | "lower_bound": 2391.8979703058926, 823 | "upper_bound": 5863.020890119689 824 | }, 825 | "point_estimate": 4352.079543536833, 826 | "standard_error": 882.4143314382903 827 | } 828 | } 829 | } 830 | } 831 | } 832 | -------------------------------------------------------------------------------- /benches/baselines/v0.4.0_bench.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base", 3 | "benchmarks": { 4 | "native addition/0": { 5 | "baseline": "base", 6 | "fullname": "base/native addition/0", 7 | "criterion_benchmark_v1": { 8 | "group_id": "native addition", 9 | "function_id": null, 10 | "value_str": "0", 11 | "throughput": null, 12 | "full_id": "native addition/0", 13 | "directory_name": "native addition/0" 14 | }, 15 | "criterion_estimates_v1": { 16 | "Mean": { 17 | "confidence_interval": { 18 | "confidence_level": 0.95, 19 | "lower_bound": 0.2962261445851212, 20 | "upper_bound": 0.29653215509710795 21 | }, 22 | "point_estimate": 0.29636478189397847, 23 | "standard_error": 0.00007865866709119933 24 | }, 25 | "Median": { 26 | "confidence_interval": { 27 | "confidence_level": 0.95, 28 | "lower_bound": 0.2960503017844731, 29 | "upper_bound": 0.2961245586618917 30 | }, 31 | "point_estimate": 0.29607952114064223, 32 | "standard_error": 0.000018668330637618655 33 | }, 34 | "MedianAbsDev": { 35 | "confidence_interval": { 36 | "confidence_level": 0.95, 37 | "lower_bound": 0.00008916117713999628, 38 | "upper_bound": 0.0002045092256151861 39 | }, 40 | "point_estimate": 0.00014926329338063905, 41 | "standard_error": 0.00002832948934390167 42 | }, 43 | "Slope": { 44 | "confidence_interval": { 45 | "confidence_level": 0.95, 46 | "lower_bound": 0.29614667997165234, 47 | "upper_bound": 0.29636869737397653 48 | }, 49 | "point_estimate": 0.296248475006578, 50 | "standard_error": 0.000056924796605270056 51 | }, 52 | "StdDev": { 53 | "confidence_interval": { 54 | "confidence_level": 0.95, 55 | "lower_bound": 0.0004223297609175888, 56 | "upper_bound": 0.0011308534895149067 57 | }, 58 | "point_estimate": 0.0007911800592864537, 59 | "standard_error": 0.0001860270660541352 60 | } 61 | } 62 | }, 63 | "native addition/1": { 64 | "baseline": "base", 65 | "fullname": "base/native addition/1", 66 | "criterion_benchmark_v1": { 67 | "group_id": "native addition", 68 | "function_id": null, 69 | "value_str": "1", 70 | "throughput": null, 71 | "full_id": "native addition/1", 72 | "directory_name": "native addition/1" 73 | }, 74 | "criterion_estimates_v1": { 75 | "Mean": { 76 | "confidence_interval": { 77 | "confidence_level": 0.95, 78 | "lower_bound": 0.29627331026653847, 79 | "upper_bound": 0.2964986772074734 80 | }, 81 | "point_estimate": 0.2963760485183063, 82 | "standard_error": 0.00005759500578483468 83 | }, 84 | "Median": { 85 | "confidence_interval": { 86 | "confidence_level": 0.95, 87 | "lower_bound": 0.2961609880588777, 88 | "upper_bound": 0.2962780290960543 89 | }, 90 | "point_estimate": 0.29620606173552433, 91 | "standard_error": 0.000028908297931034357 92 | }, 93 | "MedianAbsDev": { 94 | "confidence_interval": { 95 | "confidence_level": 0.95, 96 | "lower_bound": 0.0001587899592866076, 97 | "upper_bound": 0.0002751525996343937 98 | }, 99 | "point_estimate": 0.00022147895747918532, 100 | "standard_error": 0.0000289998173065974 101 | }, 102 | "Slope": { 103 | "confidence_interval": { 104 | "confidence_level": 0.95, 105 | "lower_bound": 0.2961798393025073, 106 | "upper_bound": 0.29637287752906466 107 | }, 108 | "point_estimate": 0.2962566477589002, 109 | "standard_error": 0.00005151575363440915 110 | }, 111 | "StdDev": { 112 | "confidence_interval": { 113 | "confidence_level": 0.95, 114 | "lower_bound": 0.00033919370526819487, 115 | "upper_bound": 0.0007755852992926167 116 | }, 117 | "point_estimate": 0.0005784933336136245, 118 | "standard_error": 0.00011134762718855966 119 | } 120 | } 121 | }, 122 | "native addition/16": { 123 | "baseline": "base", 124 | "fullname": "base/native addition/16", 125 | "criterion_benchmark_v1": { 126 | "group_id": "native addition", 127 | "function_id": null, 128 | "value_str": "16", 129 | "throughput": null, 130 | "full_id": "native addition/16", 131 | "directory_name": "native addition/16" 132 | }, 133 | "criterion_estimates_v1": { 134 | "Mean": { 135 | "confidence_interval": { 136 | "confidence_level": 0.95, 137 | "lower_bound": 0.2960536429357375, 138 | "upper_bound": 0.29622352849949346 139 | }, 140 | "point_estimate": 0.2961265649820995, 141 | "standard_error": 0.000043880269602291657 142 | }, 143 | "Median": { 144 | "confidence_interval": { 145 | "confidence_level": 0.95, 146 | "lower_bound": 0.29599517861953606, 147 | "upper_bound": 0.2960266097947223 148 | }, 149 | "point_estimate": 0.29601049529877266, 150 | "standard_error": 7.399785854711407e-6 151 | }, 152 | "MedianAbsDev": { 153 | "confidence_interval": { 154 | "confidence_level": 0.95, 155 | "lower_bound": 0.00003636132105251257, 156 | "upper_bound": 0.00007584584439626633 157 | }, 158 | "point_estimate": 0.0000567255572585545, 159 | "standard_error": 9.322061189428703e-6 160 | }, 161 | "Slope": { 162 | "confidence_interval": { 163 | "confidence_level": 0.95, 164 | "lower_bound": 0.2960183082963514, 165 | "upper_bound": 0.29606684096263175 166 | }, 167 | "point_estimate": 0.2960398295959106, 168 | "standard_error": 0.000012431601861181598 169 | }, 170 | "StdDev": { 171 | "confidence_interval": { 172 | "confidence_level": 0.95, 173 | "lower_bound": 0.0001325676375848892, 174 | "upper_bound": 0.0006699063515499536 175 | }, 176 | "point_estimate": 0.0004417266367948229, 177 | "standard_error": 0.0001425521251068037 178 | } 179 | } 180 | }, 181 | "native addition/2": { 182 | "baseline": "base", 183 | "fullname": "base/native addition/2", 184 | "criterion_benchmark_v1": { 185 | "group_id": "native addition", 186 | "function_id": null, 187 | "value_str": "2", 188 | "throughput": null, 189 | "full_id": "native addition/2", 190 | "directory_name": "native addition/2" 191 | }, 192 | "criterion_estimates_v1": { 193 | "Mean": { 194 | "confidence_interval": { 195 | "confidence_level": 0.95, 196 | "lower_bound": 0.2960056899602688, 197 | "upper_bound": 0.29615937351938776 198 | }, 199 | "point_estimate": 0.29607038710442973, 200 | "standard_error": 0.00003978028984870305 201 | }, 202 | "Median": { 203 | "confidence_interval": { 204 | "confidence_level": 0.95, 205 | "lower_bound": 0.2959770479848523, 206 | "upper_bound": 0.29599708897286087 207 | }, 208 | "point_estimate": 0.2959834181904062, 209 | "standard_error": 5.404067759959278e-6 210 | }, 211 | "MedianAbsDev": { 212 | "confidence_interval": { 213 | "confidence_level": 0.95, 214 | "lower_bound": 0.00001602640619185146, 215 | "upper_bound": 0.00003519206192554544 216 | }, 217 | "point_estimate": 0.00002380413083293452, 218 | "standard_error": 5.0033512880482366e-6 219 | }, 220 | "Slope": { 221 | "confidence_interval": { 222 | "confidence_level": 0.95, 223 | "lower_bound": 0.2959930829752187, 224 | "upper_bound": 0.2961362188874299 225 | }, 226 | "point_estimate": 0.2960412475796755, 227 | "standard_error": 0.00004135801510289805 228 | }, 229 | "StdDev": { 230 | "confidence_interval": { 231 | "confidence_level": 0.95, 232 | "lower_bound": 0.00007717391841127824, 233 | "upper_bound": 0.0006287173435694216 234 | }, 235 | "point_estimate": 0.0003990570268437357, 236 | "standard_error": 0.00013770722225541992 237 | } 238 | } 239 | }, 240 | "native addition/32": { 241 | "baseline": "base", 242 | "fullname": "base/native addition/32", 243 | "criterion_benchmark_v1": { 244 | "group_id": "native addition", 245 | "function_id": null, 246 | "value_str": "32", 247 | "throughput": null, 248 | "full_id": "native addition/32", 249 | "directory_name": "native addition/32" 250 | }, 251 | "criterion_estimates_v1": { 252 | "Mean": { 253 | "confidence_interval": { 254 | "confidence_level": 0.95, 255 | "lower_bound": 0.29618448061213687, 256 | "upper_bound": 0.2964230044515484 257 | }, 258 | "point_estimate": 0.29629455601892374, 259 | "standard_error": 0.00006124393208134967 260 | }, 261 | "Median": { 262 | "confidence_interval": { 263 | "confidence_level": 0.95, 264 | "lower_bound": 0.29599456142374225, 265 | "upper_bound": 0.29614577473849846 266 | }, 267 | "point_estimate": 0.29605693916635445, 268 | "standard_error": 0.000046427829848173056 269 | }, 270 | "MedianAbsDev": { 271 | "confidence_interval": { 272 | "confidence_level": 0.95, 273 | "lower_bound": 0.00004906282509039522, 274 | "upper_bound": 0.00025717996186190253 275 | }, 276 | "point_estimate": 0.00013299289203624567, 277 | "standard_error": 0.00006093109422114702 278 | }, 279 | "Slope": { 280 | "confidence_interval": { 281 | "confidence_level": 0.95, 282 | "lower_bound": 0.2962037200360951, 283 | "upper_bound": 0.29643933078396006 284 | }, 285 | "point_estimate": 0.29630900026690493, 286 | "standard_error": 0.00006037415509783181 287 | }, 288 | "StdDev": { 289 | "confidence_interval": { 290 | "confidence_level": 0.95, 291 | "lower_bound": 0.0003698625945367107, 292 | "upper_bound": 0.0008113650602247859 293 | }, 294 | "point_estimate": 0.0006182543992120164, 295 | "standard_error": 0.00011163482850210684 296 | } 297 | } 298 | }, 299 | "native addition/4": { 300 | "baseline": "base", 301 | "fullname": "base/native addition/4", 302 | "criterion_benchmark_v1": { 303 | "group_id": "native addition", 304 | "function_id": null, 305 | "value_str": "4", 306 | "throughput": null, 307 | "full_id": "native addition/4", 308 | "directory_name": "native addition/4" 309 | }, 310 | "criterion_estimates_v1": { 311 | "Mean": { 312 | "confidence_interval": { 313 | "confidence_level": 0.95, 314 | "lower_bound": 0.2960451211693197, 315 | "upper_bound": 0.29626002672223734 316 | }, 317 | "point_estimate": 0.2961347256366851, 318 | "standard_error": 0.00005572386836973852 319 | }, 320 | "Median": { 321 | "confidence_interval": { 322 | "confidence_level": 0.95, 323 | "lower_bound": 0.29598563628333846, 324 | "upper_bound": 0.2960118948137096 325 | }, 326 | "point_estimate": 0.29600373548350434, 327 | "standard_error": 6.862717450036348e-6 328 | }, 329 | "MedianAbsDev": { 330 | "confidence_interval": { 331 | "confidence_level": 0.95, 332 | "lower_bound": 0.0000248097324687493, 333 | "upper_bound": 0.00005423478057657614 334 | }, 335 | "point_estimate": 0.00004072574983054465, 336 | "standard_error": 7.512230819926435e-6 337 | }, 338 | "Slope": { 339 | "confidence_interval": { 340 | "confidence_level": 0.95, 341 | "lower_bound": 0.296004275789509, 342 | "upper_bound": 0.296031335809251 343 | }, 344 | "point_estimate": 0.2960163818084652, 345 | "standard_error": 6.964266043618953e-6 346 | }, 347 | "StdDev": { 348 | "confidence_interval": { 349 | "confidence_level": 0.95, 350 | "lower_bound": 0.00018197343103318735, 351 | "upper_bound": 0.0008887700725879128 352 | }, 353 | "point_estimate": 0.0005607801715188632, 354 | "standard_error": 0.0002032380609615226 355 | } 356 | } 357 | }, 358 | "native addition/8": { 359 | "baseline": "base", 360 | "fullname": "base/native addition/8", 361 | "criterion_benchmark_v1": { 362 | "group_id": "native addition", 363 | "function_id": null, 364 | "value_str": "8", 365 | "throughput": null, 366 | "full_id": "native addition/8", 367 | "directory_name": "native addition/8" 368 | }, 369 | "criterion_estimates_v1": { 370 | "Mean": { 371 | "confidence_interval": { 372 | "confidence_level": 0.95, 373 | "lower_bound": 0.2961159533259919, 374 | "upper_bound": 0.2987679308589585 375 | }, 376 | "point_estimate": 0.29703172004586953, 377 | "standard_error": 0.0008150971705870495 378 | }, 379 | "Median": { 380 | "confidence_interval": { 381 | "confidence_level": 0.95, 382 | "lower_bound": 0.29601896341259065, 383 | "upper_bound": 0.2961169270483068 384 | }, 385 | "point_estimate": 0.2960484471966944, 386 | "standard_error": 0.000021242000280743857 387 | }, 388 | "MedianAbsDev": { 389 | "confidence_interval": { 390 | "confidence_level": 0.95, 391 | "lower_bound": 0.00005232324600957407, 392 | "upper_bound": 0.00018001033295191003 393 | }, 394 | "point_estimate": 0.00009788365236839503, 395 | "standard_error": 0.000028470713397887974 396 | }, 397 | "Slope": { 398 | "confidence_interval": { 399 | "confidence_level": 0.95, 400 | "lower_bound": 0.2960677591424654, 401 | "upper_bound": 0.29613524305211847 402 | }, 403 | "point_estimate": 0.2961001560160475, 404 | "standard_error": 0.00001724256010030734 405 | }, 406 | "StdDev": { 407 | "confidence_interval": { 408 | "confidence_level": 0.95, 409 | "lower_bound": 0.00016211318000902285, 410 | "upper_bound": 0.013928964500328187 411 | }, 412 | "point_estimate": 0.008146692687470243, 413 | "standard_error": 0.0047813187791358415 414 | } 415 | } 416 | }, 417 | "λ-expression addition/0": { 418 | "baseline": "base", 419 | "fullname": "base/λ-expression addition/0", 420 | "criterion_benchmark_v1": { 421 | "group_id": "λ-expression addition", 422 | "function_id": null, 423 | "value_str": "0", 424 | "throughput": null, 425 | "full_id": "λ-expression addition/0", 426 | "directory_name": "λ-expression addition/0" 427 | }, 428 | "criterion_estimates_v1": { 429 | "Mean": { 430 | "confidence_interval": { 431 | "confidence_level": 0.95, 432 | "lower_bound": 13419.172099456906, 433 | "upper_bound": 13499.90383784224 434 | }, 435 | "point_estimate": 13447.216489721848, 436 | "standard_error": 24.98729998469355 437 | }, 438 | "Median": { 439 | "confidence_interval": { 440 | "confidence_level": 0.95, 441 | "lower_bound": 13415.42315845257, 442 | "upper_bound": 13420.241423288331 443 | }, 444 | "point_estimate": 13417.39860448684, 445 | "standard_error": 1.2294798413557133 446 | }, 447 | "MedianAbsDev": { 448 | "confidence_interval": { 449 | "confidence_level": 0.95, 450 | "lower_bound": 6.4572094529306865, 451 | "upper_bound": 11.083679094361472 452 | }, 453 | "point_estimate": 8.13761432594782, 454 | "standard_error": 1.2183816711550477 455 | }, 456 | "Slope": { 457 | "confidence_interval": { 458 | "confidence_level": 0.95, 459 | "lower_bound": 13418.846200881804, 460 | "upper_bound": 13423.50189323957 461 | }, 462 | "point_estimate": 13421.048188266588, 463 | "standard_error": 1.1891048042726475 464 | }, 465 | "StdDev": { 466 | "confidence_interval": { 467 | "confidence_level": 0.95, 468 | "lower_bound": 11.36268530908325, 469 | "upper_bound": 430.9183890775378 470 | }, 471 | "point_estimate": 251.8170260574141, 472 | "standard_error": 148.6917231770006 473 | } 474 | } 475 | }, 476 | "λ-expression addition/1": { 477 | "baseline": "base", 478 | "fullname": "base/λ-expression addition/1", 479 | "criterion_benchmark_v1": { 480 | "group_id": "λ-expression addition", 481 | "function_id": null, 482 | "value_str": "1", 483 | "throughput": null, 484 | "full_id": "λ-expression addition/1", 485 | "directory_name": "λ-expression addition/1" 486 | }, 487 | "criterion_estimates_v1": { 488 | "Mean": { 489 | "confidence_interval": { 490 | "confidence_level": 0.95, 491 | "lower_bound": 21991.15684672161, 492 | "upper_bound": 22008.084031169015 493 | }, 494 | "point_estimate": 21998.86783294481, 495 | "standard_error": 4.3344685043864795 496 | }, 497 | "Median": { 498 | "confidence_interval": { 499 | "confidence_level": 0.95, 500 | "lower_bound": 21987.70343212343, 501 | "upper_bound": 21992.296666666665 502 | }, 503 | "point_estimate": 21990.24298515105, 504 | "standard_error": 1.3418982547728602 505 | }, 506 | "MedianAbsDev": { 507 | "confidence_interval": { 508 | "confidence_level": 0.95, 509 | "lower_bound": 8.382232731939983, 510 | "upper_bound": 15.195441685781692 511 | }, 512 | "point_estimate": 11.508128466357126, 513 | "standard_error": 1.7244935663051226 514 | }, 515 | "Slope": { 516 | "confidence_interval": { 517 | "confidence_level": 0.95, 518 | "lower_bound": 21987.98126558551, 519 | "upper_bound": 22004.777581509803 520 | }, 521 | "point_estimate": 21994.63521619625, 522 | "standard_error": 4.469491312163764 523 | }, 524 | "StdDev": { 525 | "confidence_interval": { 526 | "confidence_level": 0.95, 527 | "lower_bound": 24.200588390753936, 528 | "upper_bound": 58.31545595234082 529 | }, 530 | "point_estimate": 43.50809061229699, 531 | "standard_error": 8.664066442796566 532 | } 533 | } 534 | }, 535 | "λ-expression addition/16": { 536 | "baseline": "base", 537 | "fullname": "base/λ-expression addition/16", 538 | "criterion_benchmark_v1": { 539 | "group_id": "λ-expression addition", 540 | "function_id": null, 541 | "value_str": "16", 542 | "throughput": null, 543 | "full_id": "λ-expression addition/16", 544 | "directory_name": "λ-expression addition/16" 545 | }, 546 | "criterion_estimates_v1": { 547 | "Mean": { 548 | "confidence_interval": { 549 | "confidence_level": 0.95, 550 | "lower_bound": 412258.0351927795, 551 | "upper_bound": 412588.00220630935 552 | }, 553 | "point_estimate": 412404.48395206063, 554 | "standard_error": 84.67447085271617 555 | }, 556 | "Median": { 557 | "confidence_interval": { 558 | "confidence_level": 0.95, 559 | "lower_bound": 412090.3333333333, 560 | "upper_bound": 412187.9292929293 561 | }, 562 | "point_estimate": 412140.4737762238, 563 | "standard_error": 21.76085010752117 564 | }, 565 | "MedianAbsDev": { 566 | "confidence_interval": { 567 | "confidence_level": 0.95, 568 | "lower_bound": 140.98177931525882, 569 | "upper_bound": 279.9270483391157 570 | }, 571 | "point_estimate": 202.4865132335761, 572 | "standard_error": 35.714656527223745 573 | }, 574 | "Slope": { 575 | "confidence_interval": { 576 | "confidence_level": 0.95, 577 | "lower_bound": 412205.37695592566, 578 | "upper_bound": 412424.0480659112 579 | }, 580 | "point_estimate": 412310.2869336486, 581 | "standard_error": 55.951388420699494 582 | }, 583 | "StdDev": { 584 | "confidence_interval": { 585 | "confidence_level": 0.95, 586 | "lower_bound": 421.4421778953146, 587 | "upper_bound": 1242.0586717983865 588 | }, 589 | "point_estimate": 849.8084849334746, 590 | "standard_error": 216.22975704628817 591 | } 592 | } 593 | }, 594 | "λ-expression addition/2": { 595 | "baseline": "base", 596 | "fullname": "base/λ-expression addition/2", 597 | "criterion_benchmark_v1": { 598 | "group_id": "λ-expression addition", 599 | "function_id": null, 600 | "value_str": "2", 601 | "throughput": null, 602 | "full_id": "λ-expression addition/2", 603 | "directory_name": "λ-expression addition/2" 604 | }, 605 | "criterion_estimates_v1": { 606 | "Mean": { 607 | "confidence_interval": { 608 | "confidence_level": 0.95, 609 | "lower_bound": 32717.963147283663, 610 | "upper_bound": 32795.52698823746 611 | }, 612 | "point_estimate": 32747.529310932536, 613 | "standard_error": 20.97441987762656 614 | }, 615 | "Median": { 616 | "confidence_interval": { 617 | "confidence_level": 0.95, 618 | "lower_bound": 32702.639398012383, 619 | "upper_bound": 32726.522008846696 620 | }, 621 | "point_estimate": 32713.849115504683, 622 | "standard_error": 6.082145507111555 623 | }, 624 | "MedianAbsDev": { 625 | "confidence_interval": { 626 | "confidence_level": 0.95, 627 | "lower_bound": 32.09296637165146, 628 | "upper_bound": 50.90547079169847 629 | }, 630 | "point_estimate": 43.2887443957153, 631 | "standard_error": 4.956831395218115 632 | }, 633 | "Slope": { 634 | "confidence_interval": { 635 | "confidence_level": 0.95, 636 | "lower_bound": 32703.379207555332, 637 | "upper_bound": 32721.59458187356 638 | }, 639 | "point_estimate": 32712.172760216803, 640 | "standard_error": 4.640527197246629 641 | }, 642 | "StdDev": { 643 | "confidence_interval": { 644 | "confidence_level": 0.95, 645 | "lower_bound": 44.91795637307894, 646 | "upper_bound": 352.47530171523044 647 | }, 648 | "point_estimate": 211.48491083513997, 649 | "standard_error": 100.593744450631 650 | } 651 | } 652 | }, 653 | "λ-expression addition/32": { 654 | "baseline": "base", 655 | "fullname": "base/λ-expression addition/32", 656 | "criterion_benchmark_v1": { 657 | "group_id": "λ-expression addition", 658 | "function_id": null, 659 | "value_str": "32", 660 | "throughput": null, 661 | "full_id": "λ-expression addition/32", 662 | "directory_name": "λ-expression addition/32" 663 | }, 664 | "criterion_estimates_v1": { 665 | "Mean": { 666 | "confidence_interval": { 667 | "confidence_level": 0.95, 668 | "lower_bound": 1406673.120846631, 669 | "upper_bound": 1408092.0614177466 670 | }, 671 | "point_estimate": 1407346.9691002823, 672 | "standard_error": 361.4881345452805 673 | }, 674 | "Median": { 675 | "confidence_interval": { 676 | "confidence_level": 0.95, 677 | "lower_bound": 1406782.398993808, 678 | "upper_bound": 1407518.5368421052 679 | }, 680 | "point_estimate": 1407154.2936507938, 681 | "standard_error": 185.8691110446379 682 | }, 683 | "MedianAbsDev": { 684 | "confidence_interval": { 685 | "confidence_level": 0.95, 686 | "lower_bound": 1224.6672336505724, 687 | "upper_bound": 1868.281734193091 688 | }, 689 | "point_estimate": 1496.868414451009, 690 | "standard_error": 163.99396920185893 691 | }, 692 | "Slope": { 693 | "confidence_interval": { 694 | "confidence_level": 0.95, 695 | "lower_bound": 1405997.6566173215, 696 | "upper_bound": 1407439.725864377 697 | }, 698 | "point_estimate": 1406728.1597990247, 699 | "standard_error": 366.92704799742 700 | }, 701 | "StdDev": { 702 | "confidence_interval": { 703 | "confidence_level": 0.95, 704 | "lower_bound": 2284.152562451756, 705 | "upper_bound": 4860.142358172205 706 | }, 707 | "point_estimate": 3642.6046131719786, 708 | "standard_error": 659.8483607230639 709 | } 710 | } 711 | }, 712 | "λ-expression addition/4": { 713 | "baseline": "base", 714 | "fullname": "base/λ-expression addition/4", 715 | "criterion_benchmark_v1": { 716 | "group_id": "λ-expression addition", 717 | "function_id": null, 718 | "value_str": "4", 719 | "throughput": null, 720 | "full_id": "λ-expression addition/4", 721 | "directory_name": "λ-expression addition/4" 722 | }, 723 | "criterion_estimates_v1": { 724 | "Mean": { 725 | "confidence_interval": { 726 | "confidence_level": 0.95, 727 | "lower_bound": 60489.84308240336, 728 | "upper_bound": 60549.5041205548 729 | }, 730 | "point_estimate": 60516.02490424841, 731 | "standard_error": 15.426846709313851 732 | }, 733 | "Median": { 734 | "confidence_interval": { 735 | "confidence_level": 0.95, 736 | "lower_bound": 60463.41176470588, 737 | "upper_bound": 60491.790603741494 738 | }, 739 | "point_estimate": 60474.173700591346, 740 | "standard_error": 7.795767012372237 741 | }, 742 | "MedianAbsDev": { 743 | "confidence_interval": { 744 | "confidence_level": 0.95, 745 | "lower_bound": 36.92267641104938, 746 | "upper_bound": 70.68602395514246 747 | }, 748 | "point_estimate": 51.190236411449625, 749 | "standard_error": 8.443761500299653 750 | }, 751 | "Slope": { 752 | "confidence_interval": { 753 | "confidence_level": 0.95, 754 | "lower_bound": 60503.30641090349, 755 | "upper_bound": 60533.25477066823 756 | }, 757 | "point_estimate": 60518.31268143847, 758 | "standard_error": 7.613959689786166 759 | }, 760 | "StdDev": { 761 | "confidence_interval": { 762 | "confidence_level": 0.95, 763 | "lower_bound": 60.92546224709873, 764 | "upper_bound": 232.4735539959209 765 | }, 766 | "point_estimate": 155.34286088095925, 767 | "standard_error": 45.70192464249218 768 | } 769 | } 770 | }, 771 | "λ-expression addition/8": { 772 | "baseline": "base", 773 | "fullname": "base/λ-expression addition/8", 774 | "criterion_benchmark_v1": { 775 | "group_id": "λ-expression addition", 776 | "function_id": null, 777 | "value_str": "8", 778 | "throughput": null, 779 | "full_id": "λ-expression addition/8", 780 | "directory_name": "λ-expression addition/8" 781 | }, 782 | "criterion_estimates_v1": { 783 | "Mean": { 784 | "confidence_interval": { 785 | "confidence_level": 0.95, 786 | "lower_bound": 141825.09984838014, 787 | "upper_bound": 141951.53805074206 788 | }, 789 | "point_estimate": 141880.63280450238, 790 | "standard_error": 32.57101957064142 791 | }, 792 | "Median": { 793 | "confidence_interval": { 794 | "confidence_level": 0.95, 795 | "lower_bound": 141793.3192481203, 796 | "upper_bound": 141822.87394957984 797 | }, 798 | "point_estimate": 141801.0717189315, 799 | "standard_error": 9.013766113564763 800 | }, 801 | "MedianAbsDev": { 802 | "confidence_interval": { 803 | "confidence_level": 0.95, 804 | "lower_bound": 34.79895189055458, 805 | "upper_bound": 57.93287118699164 806 | }, 807 | "point_estimate": 45.22943033140649, 808 | "standard_error": 5.991652159965814 809 | }, 810 | "Slope": { 811 | "confidence_interval": { 812 | "confidence_level": 0.95, 813 | "lower_bound": 141795.4526169362, 814 | "upper_bound": 141824.79582209594 815 | }, 816 | "point_estimate": 141809.04570921912, 817 | "standard_error": 7.4709802381494 818 | }, 819 | "StdDev": { 820 | "confidence_interval": { 821 | "confidence_level": 0.95, 822 | "lower_bound": 115.44644380644294, 823 | "upper_bound": 476.3764202362789 824 | }, 825 | "point_estimate": 326.94680597276704, 826 | "standard_error": 89.09743151821272 827 | } 828 | } 829 | } 830 | } 831 | } 832 | --------------------------------------------------------------------------------