feat: lambda expressions are mutable now

This commit is contained in:
2026-02-09 19:52:09 -05:00
parent 08bf248745
commit 9d44f5433c
8 changed files with 67 additions and 98 deletions

View File

@@ -10,7 +10,7 @@ import (
) )
func encodeAtom(n *saccharine.Atom) lambda.Expression { func encodeAtom(n *saccharine.Atom) lambda.Expression {
return lambda.NewVariable(n.Name) return lambda.Variable{Name: n.Name}
} }
func encodeAbstraction(n *saccharine.Abstraction) lambda.Expression { func encodeAbstraction(n *saccharine.Abstraction) lambda.Expression {
@@ -27,7 +27,7 @@ func encodeAbstraction(n *saccharine.Abstraction) lambda.Expression {
} }
for i := len(parameters) - 1; i >= 0; i-- { for i := len(parameters) - 1; i >= 0; i-- {
result = lambda.NewAbstraction(parameters[i], result) result = lambda.Abstraction{Parameter: parameters[i], Body: result}
} }
return result return result
@@ -43,7 +43,7 @@ func encodeApplication(n *saccharine.Application) lambda.Expression {
} }
for _, argument := range arguments { for _, argument := range arguments {
result = lambda.NewApplication(result, argument) result = lambda.Application{Abstraction: result, Argument: argument}
} }
return result return result
@@ -58,19 +58,19 @@ func reduceLet(s *saccharine.LetStatement, e lambda.Expression) lambda.Expressio
value = encodeAbstraction(&saccharine.Abstraction{Parameters: s.Parameters, Body: s.Body}) value = encodeAbstraction(&saccharine.Abstraction{Parameters: s.Parameters, Body: s.Body})
} }
return lambda.NewApplication( return lambda.Application{
lambda.NewAbstraction(s.Name, e), Abstraction: lambda.Abstraction{Parameter: s.Name, Body: e},
value, Argument: value,
) }
} }
func reduceDeclare(s *saccharine.DeclareStatement, e lambda.Expression) lambda.Expression { func reduceDeclare(s *saccharine.DeclareStatement, e lambda.Expression) lambda.Expression {
freshVar := lambda.GenerateFreshName(e.GetFree()) freshVar := lambda.GenerateFreshName(e.GetFree())
return lambda.NewApplication( return lambda.Application{
lambda.NewAbstraction(freshVar, e), Abstraction: lambda.Abstraction{Parameter: freshVar, Body: e},
encodeExpression(s.Value), Argument: encodeExpression(s.Value),
) }
} }
func reduceStatement(s saccharine.Statement, e lambda.Expression) lambda.Expression { func reduceStatement(s saccharine.Statement, e lambda.Expression) lambda.Expression {
@@ -112,15 +112,15 @@ func encodeExpression(s saccharine.Expression) lambda.Expression {
func decodeExression(l lambda.Expression) saccharine.Expression { func decodeExression(l lambda.Expression) saccharine.Expression {
switch l := l.(type) { switch l := l.(type) {
case lambda.Variable: case lambda.Variable:
return &saccharine.Atom{Name: l.Name()} return &saccharine.Atom{Name: l.Name}
case lambda.Abstraction: case lambda.Abstraction:
return &saccharine.Abstraction{ return &saccharine.Abstraction{
Parameters: []string{l.Parameter()}, Parameters: []string{l.Parameter},
Body: decodeExression(l.Body())} Body: decodeExression(l.Body)}
case lambda.Application: case lambda.Application:
return &saccharine.Application{ return &saccharine.Application{
Abstraction: decodeExression(l.Abstraction()), Abstraction: decodeExression(l.Abstraction),
Arguments: []saccharine.Expression{decodeExression(l.Argument())}} Arguments: []saccharine.Expression{decodeExression(l.Argument)}}
default: default:
panic(fmt.Errorf("unknown expression type: %T", l)) panic(fmt.Errorf("unknown expression type: %T", l))
} }

View File

@@ -10,25 +10,25 @@ import "git.maximhutz.com/max/lambda/pkg/lambda"
func ReduceOnce(e lambda.Expression) (lambda.Expression, bool) { func ReduceOnce(e lambda.Expression) (lambda.Expression, bool) {
switch e := e.(type) { switch e := e.(type) {
case lambda.Abstraction: case lambda.Abstraction:
body, reduced := ReduceOnce(e.Body()) body, reduced := ReduceOnce(e.Body)
if reduced { if reduced {
return lambda.NewAbstraction(e.Parameter(), body), true return lambda.Abstraction{Parameter: e.Parameter, Body: body}, true
} }
return e, false return e, false
case lambda.Application: case lambda.Application:
if fn, fnOk := e.Abstraction().(lambda.Abstraction); fnOk { if fn, fnOk := e.Abstraction.(lambda.Abstraction); fnOk {
return fn.Body().Substitute(fn.Parameter(), e.Argument()), true return fn.Body.Substitute(fn.Parameter, e.Argument), true
} }
abs, reduced := ReduceOnce(e.Abstraction()) abs, reduced := ReduceOnce(e.Abstraction)
if reduced { if reduced {
return lambda.NewApplication(abs, e.Argument()), true return lambda.Application{Abstraction: abs, Argument: e.Argument}, true
} }
arg, reduced := ReduceOnce(e.Argument()) arg, reduced := ReduceOnce(e.Argument)
if reduced { if reduced {
return lambda.NewApplication(e.Abstraction(), arg), true return lambda.Application{Abstraction: e.Abstraction, Argument: arg}, true
} }
return e, false return e, false

View File

@@ -3,17 +3,17 @@ package lambda
import "git.maximhutz.com/max/lambda/pkg/set" import "git.maximhutz.com/max/lambda/pkg/set"
func (e Variable) GetFree() set.Set[string] { func (e Variable) GetFree() set.Set[string] {
return set.New(e.Name()) return set.New(e.Name)
} }
func (e Abstraction) GetFree() set.Set[string] { func (e Abstraction) GetFree() set.Set[string] {
vars := e.Body().GetFree() vars := e.Body.GetFree()
vars.Remove(e.Parameter()) vars.Remove(e.Parameter)
return vars return vars
} }
func (e Application) GetFree() set.Set[string] { func (e Application) GetFree() set.Set[string] {
vars := e.Abstraction().GetFree() vars := e.Abstraction.GetFree()
vars.Merge(e.Argument().GetFree()) vars.Merge(e.Argument.GetFree())
return vars return vars
} }

View File

@@ -1,12 +1,12 @@
package lambda package lambda
func (e Variable) IsFree(n string) bool { func (e Variable) IsFree(n string) bool {
return e.Name() == n return e.Name == n
} }
func (e Abstraction) IsFree(n string) bool { func (e Abstraction) IsFree(n string) bool {
return e.Parameter() != n && e.Body().IsFree(n) return e.Parameter != n && e.Body.IsFree(n)
} }
func (e Application) IsFree(n string) bool { func (e Application) IsFree(n string) bool {
return e.Abstraction().IsFree(n) || e.Argument().IsFree(n) return e.Abstraction.IsFree(n) || e.Argument.IsFree(n)
} }

View File

@@ -32,69 +32,25 @@ type Expression interface {
/** ------------------------------------------------------------------------- */ /** ------------------------------------------------------------------------- */
type Abstraction struct { type Abstraction struct {
parameter string Parameter string
body Expression Body Expression
} }
var _ Expression = Abstraction{} var _ Expression = Abstraction{}
func (a Abstraction) Parameter() string {
return a.parameter
}
func (a Abstraction) Body() Expression {
return a.body
}
func (a Abstraction) String() string {
return "\\" + a.parameter + "." + a.body.String()
}
func NewAbstraction(parameter string, body Expression) Abstraction {
return Abstraction{parameter, body}
}
/** ------------------------------------------------------------------------- */ /** ------------------------------------------------------------------------- */
type Application struct { type Application struct {
abstraction Expression Abstraction Expression
argument Expression Argument Expression
} }
var _ Expression = Application{} var _ Expression = Application{}
func (a Application) Abstraction() Expression {
return a.abstraction
}
func (a Application) Argument() Expression {
return a.argument
}
func (a Application) String() string {
return "(" + a.abstraction.String() + " " + a.argument.String() + ")"
}
func NewApplication(abstraction Expression, argument Expression) Application {
return Application{abstraction, argument}
}
/** ------------------------------------------------------------------------- */ /** ------------------------------------------------------------------------- */
type Variable struct { type Variable struct {
name string Name string
} }
var _ Expression = Variable{} var _ Expression = Variable{}
func (v Variable) Name() string {
return v.name
}
func (v Variable) String() string {
return v.name
}
func NewVariable(name string) Variable {
return Variable{name}
}

View File

@@ -2,27 +2,27 @@ package lambda
// Rename replaces all occurrences of the target variable name with the new name. // Rename replaces all occurrences of the target variable name with the new name.
func (e Variable) Rename(target string, newName string) Expression { func (e Variable) Rename(target string, newName string) Expression {
if e.Name() == target { if e.Name == target {
return NewVariable(newName) return Variable{Name: newName}
} }
return e return e
} }
func (e Abstraction) Rename(target string, newName string) Expression { func (e Abstraction) Rename(target string, newName string) Expression {
newParam := e.Parameter() newParam := e.Parameter
if e.Parameter() == target { if e.Parameter == target {
newParam = newName newParam = newName
} }
newBody := e.Body().Rename(target, newName) newBody := e.Body.Rename(target, newName)
return NewAbstraction(newParam, newBody) return Abstraction{Parameter: newParam, Body: newBody}
} }
func (e Application) Rename(target string, newName string) Expression { func (e Application) Rename(target string, newName string) Expression {
newAbs := e.Abstraction().Rename(target, newName) newAbs := e.Abstraction.Rename(target, newName)
newArg := e.Argument().Rename(target, newName) newArg := e.Argument.Rename(target, newName)
return NewApplication(newAbs, newArg) return Application{Abstraction: newAbs, Argument: newArg}
} }

13
pkg/lambda/stringify.go Normal file
View File

@@ -0,0 +1,13 @@
package lambda
func (a Abstraction) String() string {
return "\\" + a.Parameter + "." + a.Body.String()
}
func (a Application) String() string {
return "(" + a.Abstraction.String() + " " + a.Argument.String() + ")"
}
func (v Variable) String() string {
return v.Name
}

View File

@@ -1,7 +1,7 @@
package lambda package lambda
func (e Variable) Substitute(target string, replacement Expression) Expression { func (e Variable) Substitute(target string, replacement Expression) Expression {
if e.Name() == target { if e.Name == target {
return replacement return replacement
} }
@@ -9,12 +9,12 @@ func (e Variable) Substitute(target string, replacement Expression) Expression {
} }
func (e Abstraction) Substitute(target string, replacement Expression) Expression { func (e Abstraction) Substitute(target string, replacement Expression) Expression {
if e.Parameter() == target { if e.Parameter == target {
return e return e
} }
body := e.Body() body := e.Body
param := e.Parameter() param := e.Parameter
if replacement.IsFree(param) { if replacement.IsFree(param) {
freeVars := replacement.GetFree() freeVars := replacement.GetFree()
freeVars.Merge(body.GetFree()) freeVars.Merge(body.GetFree())
@@ -24,12 +24,12 @@ func (e Abstraction) Substitute(target string, replacement Expression) Expressio
} }
newBody := body.Substitute(target, replacement) newBody := body.Substitute(target, replacement)
return NewAbstraction(param, newBody) return Abstraction{Parameter: param, Body: newBody}
} }
func (e Application) Substitute(target string, replacement Expression) Expression { func (e Application) Substitute(target string, replacement Expression) Expression {
abs := e.Abstraction().Substitute(target, replacement) abs := e.Abstraction.Substitute(target, replacement)
arg := e.Argument().Substitute(target, replacement) arg := e.Argument.Substitute(target, replacement)
return NewApplication(abs, arg) return Application{Abstraction: abs, Argument: arg}
} }