From a967410af77040951360a6c4d4631e4b4f8684d6 Mon Sep 17 00:00:00 2001 From: "M.V. Hutz" Date: Sat, 10 Jan 2026 21:01:47 -0500 Subject: [PATCH] refactor: convert Substitute and Rename to standalone functions Remove Substitute and Rename methods from Expression interface. Refactor receiver methods into standalone functions using type switching. Update call sites to use new function signatures. Co-Authored-By: Claude Sonnet 4.5 --- pkg/lambda/expression.go | 2 - pkg/lambda/reduce.go | 2 +- pkg/lambda/rename.go | 66 ++++++++++++++++---------------- pkg/lambda/substitute.go | 82 ++++++++++++++++++++-------------------- 4 files changed, 77 insertions(+), 75 deletions(-) diff --git a/pkg/lambda/expression.go b/pkg/lambda/expression.go index 5055986..d9678d2 100644 --- a/pkg/lambda/expression.go +++ b/pkg/lambda/expression.go @@ -3,8 +3,6 @@ package lambda // Expression represents an immutable lambda calculus expression. type Expression interface { Accept(Visitor) - Substitute(target string, replacement Expression) Expression - Rename(target string, newName string) Expression } /** ------------------------------------------------------------------------- */ diff --git a/pkg/lambda/reduce.go b/pkg/lambda/reduce.go index 2df9a9d..74deba2 100644 --- a/pkg/lambda/reduce.go +++ b/pkg/lambda/reduce.go @@ -13,7 +13,7 @@ func ReduceOnce(e *Expression) bool { stack.Push(&typed.body) case *Application: if fn, fnOk := typed.function.(*Abstraction); fnOk { - reduced := fn.body.Substitute(fn.parameter, typed.argument) + reduced := Substitute(fn.body, fn.parameter, typed.argument) *top = reduced return true } diff --git a/pkg/lambda/rename.go b/pkg/lambda/rename.go index 2c9b329..3a1d687 100644 --- a/pkg/lambda/rename.go +++ b/pkg/lambda/rename.go @@ -1,37 +1,39 @@ package lambda // Rename replaces all occurrences of target variable with newName. -func (v *Variable) Rename(target string, newName string) Expression { - if v.value == target { - return NewVariable(newName) +func Rename(expr Expression, target string, newName string) Expression { + switch e := expr.(type) { + case *Variable: + if e.value == target { + return NewVariable(newName) + } + return e + + case *Abstraction: + newParam := e.parameter + if e.parameter == target { + newParam = newName + } + + newBody := Rename(e.body, target, newName) + + if newParam == e.parameter && newBody == e.body { + return e + } + + return NewAbstraction(newParam, newBody) + + case *Application: + newFunc := Rename(e.function, target, newName) + newArg := Rename(e.argument, target, newName) + + if newFunc == e.function && newArg == e.argument { + return e + } + + return NewApplication(newFunc, newArg) + + default: + return expr } - return v -} - -// Rename replaces all occurrences of target variable with newName. -func (a *Abstraction) Rename(target string, newName string) Expression { - newParam := a.parameter - if a.parameter == target { - newParam = newName - } - - newBody := a.body.Rename(target, newName) - - if newParam == a.parameter && newBody == a.body { - return a - } - - return NewAbstraction(newParam, newBody) -} - -// Rename replaces all occurrences of target variable with newName. -func (a *Application) Rename(target string, newName string) Expression { - newFunc := a.function.Rename(target, newName) - newArg := a.argument.Rename(target, newName) - - if newFunc == a.function && newArg == a.argument { - return a - } - - return NewApplication(newFunc, newArg) } diff --git a/pkg/lambda/substitute.go b/pkg/lambda/substitute.go index e0ef99d..d2bb3d9 100644 --- a/pkg/lambda/substitute.go +++ b/pkg/lambda/substitute.go @@ -1,45 +1,47 @@ package lambda // Substitute replaces all free occurrences of target with replacement. -func (v *Variable) Substitute(target string, replacement Expression) Expression { - if v.value == target { - return replacement +func Substitute(expr Expression, target string, replacement Expression) Expression { + switch e := expr.(type) { + case *Variable: + if e.value == target { + return replacement + } + return e + + case *Abstraction: + if e.parameter == target { + return e + } + + body := e.body + param := e.parameter + if IsFreeVariable(param, replacement) { + freeVars := GetFreeVariables(replacement) + freeVars.Merge(GetFreeVariables(body)) + freshVar := GenerateFreshName(freeVars) + body = Rename(body, param, freshVar) + param = freshVar + } + + newBody := Substitute(body, target, replacement) + if newBody == body && param == e.parameter { + return e + } + + return NewAbstraction(param, newBody) + + case *Application: + newFunc := Substitute(e.function, target, replacement) + newArg := Substitute(e.argument, target, replacement) + + if newFunc == e.function && newArg == e.argument { + return e + } + + return NewApplication(newFunc, newArg) + + default: + return expr } - return v -} - -// Substitute replaces all free occurrences of target with replacement. -func (a *Abstraction) Substitute(target string, replacement Expression) Expression { - if a.parameter == target { - return a - } - - body := a.body - param := a.parameter - if IsFreeVariable(param, replacement) { - freeVars := GetFreeVariables(replacement) - freeVars.Merge(GetFreeVariables(body)) - freshVar := GenerateFreshName(freeVars) - body = body.Rename(param, freshVar) - param = freshVar - } - - newBody := body.Substitute(target, replacement) - if newBody == body && param == a.parameter { - return a - } - - return NewAbstraction(param, newBody) -} - -// Substitute replaces all free occurrences of target with replacement. -func (a *Application) Substitute(target string, replacement Expression) Expression { - newFunc := a.function.Substitute(target, replacement) - newArg := a.argument.Substitute(target, replacement) - - if newFunc == a.function && newArg == a.argument { - return a - } - - return NewApplication(newFunc, newArg) }