perf: implement structural sharing for expression trees #10

Merged
mvhutz merged 5 commits from perf/structural-sharing into main 2026-01-11 02:15:38 +00:00
4 changed files with 77 additions and 75 deletions
Showing only changes of commit a967410af7 - Show all commits

View File

@@ -3,8 +3,6 @@ package lambda
// Expression represents an immutable lambda calculus expression. // Expression represents an immutable lambda calculus expression.
type Expression interface { type Expression interface {
Accept(Visitor) Accept(Visitor)
Substitute(target string, replacement Expression) Expression
Rename(target string, newName string) Expression
} }
/** ------------------------------------------------------------------------- */ /** ------------------------------------------------------------------------- */

View File

@@ -13,7 +13,7 @@ func ReduceOnce(e *Expression) bool {
stack.Push(&typed.body) stack.Push(&typed.body)
case *Application: case *Application:
if fn, fnOk := typed.function.(*Abstraction); fnOk { 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 *top = reduced
return true return true
} }

View File

@@ -1,37 +1,39 @@
package lambda package lambda
// Rename replaces all occurrences of target variable with newName. // Rename replaces all occurrences of target variable with newName.
func (v *Variable) Rename(target string, newName string) Expression { func Rename(expr Expression, target string, newName string) Expression {
if v.value == target { switch e := expr.(type) {
case *Variable:
if e.value == target {
return NewVariable(newName) return NewVariable(newName)
} }
return v return e
}
// Rename replaces all occurrences of target variable with newName. case *Abstraction:
func (a *Abstraction) Rename(target string, newName string) Expression { newParam := e.parameter
newParam := a.parameter if e.parameter == target {
if a.parameter == target {
newParam = newName newParam = newName
} }
newBody := a.body.Rename(target, newName) newBody := Rename(e.body, target, newName)
if newParam == a.parameter && newBody == a.body { if newParam == e.parameter && newBody == e.body {
return a return e
} }
return NewAbstraction(newParam, newBody) return NewAbstraction(newParam, newBody)
}
// Rename replaces all occurrences of target variable with newName. case *Application:
func (a *Application) Rename(target string, newName string) Expression { newFunc := Rename(e.function, target, newName)
newFunc := a.function.Rename(target, newName) newArg := Rename(e.argument, target, newName)
newArg := a.argument.Rename(target, newName)
if newFunc == a.function && newArg == a.argument { if newFunc == e.function && newArg == e.argument {
return a return e
} }
return NewApplication(newFunc, newArg) return NewApplication(newFunc, newArg)
default:
return expr
}
} }

View File

@@ -1,45 +1,47 @@
package lambda package lambda
// Substitute replaces all free occurrences of target with replacement. // Substitute replaces all free occurrences of target with replacement.
func (v *Variable) Substitute(target string, replacement Expression) Expression { func Substitute(expr Expression, target string, replacement Expression) Expression {
if v.value == target { switch e := expr.(type) {
case *Variable:
if e.value == target {
return replacement return replacement
} }
return v return e
}
// Substitute replaces all free occurrences of target with replacement. case *Abstraction:
func (a *Abstraction) Substitute(target string, replacement Expression) Expression { if e.parameter == target {
if a.parameter == target { return e
return a
} }
body := a.body body := e.body
param := a.parameter param := e.parameter
if IsFreeVariable(param, replacement) { if IsFreeVariable(param, replacement) {
freeVars := GetFreeVariables(replacement) freeVars := GetFreeVariables(replacement)
freeVars.Merge(GetFreeVariables(body)) freeVars.Merge(GetFreeVariables(body))
freshVar := GenerateFreshName(freeVars) freshVar := GenerateFreshName(freeVars)
body = body.Rename(param, freshVar) body = Rename(body, param, freshVar)
param = freshVar param = freshVar
} }
newBody := body.Substitute(target, replacement) newBody := Substitute(body, target, replacement)
if newBody == body && param == a.parameter { if newBody == body && param == e.parameter {
return a return e
} }
return NewAbstraction(param, newBody) return NewAbstraction(param, newBody)
}
// Substitute replaces all free occurrences of target with replacement. case *Application:
func (a *Application) Substitute(target string, replacement Expression) Expression { newFunc := Substitute(e.function, target, replacement)
newFunc := a.function.Substitute(target, replacement) newArg := Substitute(e.argument, target, replacement)
newArg := a.argument.Substitute(target, replacement)
if newFunc == a.function && newArg == a.argument { if newFunc == e.function && newArg == e.argument {
return a return e
} }
return NewApplication(newFunc, newArg) return NewApplication(newFunc, newArg)
default:
return expr
}
} }