Implement basic beta reduction

This commit is contained in:
Gabriel Tofvesson 2025-08-24 14:06:26 +02:00
parent 0706056111
commit f6fbe890b5

View File

@ -1,113 +1,169 @@
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug)]
struct ArgCell(Rc<RefCell<Option<EitherValue>>>);
#[derive(Debug)]
struct Lambda {
arg: ArgCell,
expression: Expression
}
#[derive(Debug)]
enum EitherValue {
Lambda(Lambda),
Expression(Expression)
}
use std::collections::HashMap;
#[derive(Debug, Clone)]
enum Expression {
Valued(ArgCell),
Expression(ArgCell, ArgCell)
pub struct ArgGen(usize);
impl ArgGen {
pub fn new() -> Self {
ArgGen(0)
}
impl ArgCell {
fn lambda(value: Lambda) -> Self {
ArgCell(Rc::new(RefCell::new(Some(EitherValue::Lambda(value)))))
}
fn expression(expression: Expression) -> Self {
ArgCell(Rc::new(RefCell::new(Some(EitherValue::Expression(expression)))))
}
fn future() -> Self {
ArgCell(Rc::new(RefCell::new(None)))
}
fn copy_retain(&self) -> Self {
Self(self.0.clone())
pub fn next(&mut self) -> usize {
let current = self.0;
self.0 += 1;
current
}
}
impl Clone for ArgCell {
fn clone(&self) -> Self {
let cell = self.0.borrow();
if cell.is_some() {
Self(self.0.clone())
} else {
Self(Rc::new(RefCell::new(None)))
}
}
}
impl Lambda {
fn new(expression: impl FnOnce(ArgCell) -> Expression) -> Self {
let cell = ArgCell::future();
Self {
arg: cell.copy_retain(),
expression: expression(cell)
}
}
fn beta_reduce(self, argument: Option<EitherValue>) -> Expression {
*self.arg.0.borrow_mut() = argument;
self.expression
}
}
impl Clone for Lambda {
fn clone(&self) -> Self {
Self::new(|_| self.expression.clone())
}
}
impl EitherValue {
fn is_lambda(&self) -> bool {
matches!(*self, EitherValue::Lambda(_))
}
fn is_expression(&self) -> bool {
matches!(*self, EitherValue::Expression(_))
}
#[derive(Debug)]
pub enum Expression {
Paren(Box<Expression>),
Variable(usize),
Call(Box<Expression>, Box<Expression>),
Lambda(usize, Box<Expression>),
}
impl Expression {
fn new_valued(value: ArgCell) -> Self {
Self::Valued(value)
pub fn paren(expr: Expression) -> Self {
Expression::Paren(Box::new(expr))
}
fn new_expression(callee: ArgCell, argument: ArgCell) -> Self {
Self::Expression(callee, argument)
pub fn variable(index: usize) -> Self {
Expression::Variable(index)
}
fn can_beta_reduce(&self) -> bool {
// TODO: Clean this up
matches!(self, Self::Expression(callee, _) if callee.0.borrow().is_some() && callee.0.borrow().as_ref().unwrap().is_lambda())
pub fn call(func: Expression, arg: Expression) -> Self {
Expression::Call(Box::new(func), Box::new(arg))
}
pub fn lambda(arg_index: usize, body: Expression) -> Self {
Expression::Lambda(arg_index, Box::new(body))
}
pub fn deep_clone(&self, arg_map: &mut HashMap<usize, usize>, arg_gen: &mut ArgGen) -> Self {
match self {
Expression::Paren(expr) => Expression::paren(expr.deep_clone(arg_map, arg_gen)),
Expression::Variable(i) => {
if let Some(new_index) = arg_map.get(i) {
Expression::variable(*new_index)
} else {
Expression::variable(*i)
}
}
Expression::Call(func, arg) => Expression::call(
func.deep_clone(arg_map, arg_gen),
arg.deep_clone(arg_map, arg_gen),
),
Expression::Lambda(arg_index, body) => {
let new_index = arg_gen.next();
arg_map.insert(*arg_index, new_index);
let cloned_body = body.deep_clone(arg_map, arg_gen);
arg_map.remove(arg_index);
Expression::Lambda(new_index, Box::new(cloned_body))
}
}
}
// (lx.(ly.x(xy))) (lz.z)
// -> (ly.(lz.z)((lw.w)y))
// -> (ly.(lz.z)y)
// -> (ly.y)
pub fn substitute(&self, index: usize, replacement: &Expression, arg_gen: &mut ArgGen) -> Expression {
match self {
Expression::Paren(expr) => Expression::paren(expr.substitute(index, replacement, arg_gen)),
Expression::Variable(i) => {
if *i == index {
replacement.deep_clone(&mut HashMap::new(), arg_gen)
} else {
Expression::variable(*i)
}
}
Expression::Call(func, arg) => {
if let Expression::Lambda(arg_index, body) = &**func {
if *arg_index == index {
let new_body = body.substitute(index, replacement, arg_gen);
let new_arg = arg.substitute(index, replacement, arg_gen);
new_body.substitute(*arg_index, &new_arg, arg_gen)
} else {
Expression::call(
func.substitute(index, replacement, arg_gen),
arg.substitute(index, replacement, arg_gen),
)
}
} else {
Expression::call(
func.substitute(index, replacement, arg_gen),
arg.substitute(index, replacement, arg_gen),
)
}
},
Expression::Lambda(arg_index, body) => {
if *arg_index == index {
body.substitute(index, replacement, arg_gen)
} else {
Expression::Lambda(
*arg_index,
Box::new(body.substitute(index, replacement, arg_gen)),
)
}
}
}
}
pub fn find_next_reducible(&self) -> Option<(usize, &Expression)> {
match self {
Expression::Call(func, arg) => match **func {
Expression::Lambda(arg_index, _) => Some((arg_index, arg.as_ref())),
_ => func.find_next_reducible().or_else(|| arg.find_next_reducible()),
},
Expression::Paren(expr) => expr.find_next_reducible(),
Expression::Lambda(_, body) => body.find_next_reducible(),
Expression::Variable(_) => None,
}
}
}
impl ToString for Expression {
fn to_string(&self) -> String {
match self {
Expression::Paren(expr) => format!("{{{}}}", expr.to_string()),
Expression::Variable(i) => format!("x{}", i),
Expression::Call(func, arg) => format!("({} {})", func.to_string(), arg.to_string()),
Expression::Lambda(arg_index, body) => format!("[λx{}. {}]", arg_index, body.to_string()),
}
}
}
fn main() {
// Lambda_x { Expression { Lambda_y { Expression { x(Expression(x, y)) } } } }
let func = Lambda::new(|x|
Expression::new_valued(ArgCell::lambda(Lambda::new(|y| Expression::new_expression(x.copy_retain(), ArgCell::expression(Expression::Expression(x, y))))))
);
let func_identity = Some(EitherValue::Lambda(Lambda::new(Expression::Valued)));
let reduced = func.beta_reduce(func_identity);
dbg!(reduced);
let mut arg_gen = &mut ArgGen::new();
let a = arg_gen.next();
let b = arg_gen.next();
let c = arg_gen.next();
let s1 = Expression::lambda(a, Expression::lambda(b, Expression::lambda(c,
Expression::call(
Expression::variable(b),
Expression::call(
Expression::call(
Expression::variable(a),
Expression::variable(b)
),
Expression::variable(c),
),
))));
let s2 = Expression::call(s1.deep_clone(&mut HashMap::new(), &mut arg_gen), s1.deep_clone(&mut HashMap::new(), &mut arg_gen));
let s3 = Expression::call(s1.deep_clone(&mut HashMap::new(), &mut arg_gen), s2.deep_clone(&mut HashMap::new(), &mut arg_gen));
let z = arg_gen.next();
let w = arg_gen.next();
let zero = Expression::lambda(z, Expression::lambda(w, Expression::variable(w)));
let one = Expression::lambda(z, Expression::lambda(w, Expression::call(Expression::variable(z), Expression::variable(w))));
let two = Expression::lambda(z, Expression::lambda(w, Expression::call(Expression::variable(z), Expression::call(Expression::variable(z), Expression::variable(w)))));
let three = Expression::lambda(z, Expression::lambda(w, Expression::call(Expression::variable(z), Expression::call(Expression::variable(z), Expression::call(Expression::variable(z), Expression::variable(w))))));
let four = Expression::lambda(z, Expression::lambda(w, Expression::call(Expression::variable(z), Expression::call(Expression::variable(z), Expression::call(Expression::variable(z), Expression::call(Expression::variable(z), Expression::variable(w)))))));
let mut full_expr = Expression::call(s3, two);
println!("Solving: {}", full_expr.to_string());
while let Some((index, reduced)) = full_expr.find_next_reducible() {
full_expr = full_expr.substitute(index, reduced, &mut arg_gen);
}
println!("-> {}", full_expr.to_string());
}