refactor: extract Reducer interface and update engine to use abstractions (#31)

## Description

This PR builds on #30 to complete the abstraction layer for multi-mode evaluation support.
The engine now accepts abstract `expr.Expression` and `reducer.Reducer` types instead of concrete lambda types.

- Add `pkg/reducer/reducer.go` with `Reducer` interface defining `Reduce(expr.Expression, onStep) expr.Expression`.
- Add `pkg/lambda/reducer.go` with `NormalOrderReducer` that wraps the existing `ReduceAll` logic.
- Update `engine.Engine` to store `expr.Expression` and `reducer.Reducer` instead of `*lambda.Expression`.
- Update plugins to use `expr.Expression.String()` directly (no pointer dereference needed).
- Update main and tests to instantiate `NormalOrderReducer` and pass it to the engine.

### Decisions

- The `Reducer.Reduce` method returns the final expression and calls `onStep` after each reduction step with the current state.
- `NormalOrderReducer` type-asserts to `lambda.Expression` internally; other expression types are returned unchanged.
- The engine updates its `Expression` field both during reduction (via `onStep`) and after completion.

## Benefits

- The engine is now fully decoupled from lambda-specific types.
- New evaluation modes can be added by implementing `expr.Expression` and `reducer.Reducer`.
- Plugins work with any expression type that implements `expr.Expression`.
- Prepares the codebase for SKI combinators, typed lambda calculus, or other future modes.

## Checklist

- [x] Code follows conventional commit format.
- [x] Branch follows naming convention (`<type>/<description>`).
- [x] Tests pass (if applicable).
- [ ] Documentation updated (if applicable).

Closes #30

Reviewed-on: #31
Co-authored-by: M.V. Hutz <git@maximhutz.me>
Co-committed-by: M.V. Hutz <git@maximhutz.me>
This commit was merged in pull request #31.
This commit is contained in:
2026-01-16 23:42:07 +00:00
committed by Maxim Hutz
parent e0114c736d
commit f8e1223463
7 changed files with 71 additions and 16 deletions

View File

@@ -8,6 +8,7 @@ import (
"git.maximhutz.com/max/lambda/internal/engine"
"git.maximhutz.com/max/lambda/internal/plugins"
"git.maximhutz.com/max/lambda/pkg/convert"
"git.maximhutz.com/max/lambda/pkg/lambda"
"git.maximhutz.com/max/lambda/pkg/saccharine"
)
@@ -33,8 +34,9 @@ func main() {
compiled := convert.SaccharineToLambda(ast)
logger.Info("compiled λ expression", "tree", compiled.String())
// Create reduction engine.
process := engine.New(options, &compiled)
// Create reduction engine with normal order reducer.
reducer := lambda.NewNormalOrderReducer()
process := engine.New(options, compiled, reducer)
// If the user selected to track CPU performance, attach a profiler to the
// process.
@@ -61,7 +63,7 @@ func main() {
process.Run()
// Return the final reduced result.
result := (*process.Expression).String()
result := process.Expression.String()
err = options.Destination.Write(result)
cli.HandleError(err)
}

View File

@@ -41,11 +41,12 @@ func runSample(samplePath string) (string, error) {
Verbose: false,
}
// Create and run the engine.
process := engine.New(cfg, &compiled)
// Create and run the engine with normal order reducer.
reducer := lambda.NewNormalOrderReducer()
process := engine.New(cfg, compiled, reducer)
process.Run()
return lambda.Stringify(compiled) + "\n", nil
return process.Expression.String() + "\n", nil
}
// Test that all samples produce expected output.