Move the event emitter and lifecycle events from the engine into the reducer,
making the reducer the single point of orchestration for reduction.
This eliminates the engine package entirely.
- Add events.go to pkg/reducer with Start, Step, and Stop events.
- Extend Reducer interface to embed Emitter and add Expression() method.
- Update NormalOrderReducer to embed BaseEmitter and emit lifecycle events.
- Update all plugins to attach to Reducer instead of Engine.
- Remove internal/engine package.
- Add Off() method to BaseEmitter to complete Emitter interface.
- Fix Emitter.On signature to use generic type E instead of string.
## 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>
## Description
The codebase currently couples the engine and plugins directly to `lambda.Expression`.
This PR introduces an abstract `expr.Expression` interface to enable future support for multiple evaluation modes.
- Add `pkg/expr/expr.go` with an `Expression` interface requiring a `String()` method.
- Update `lambda.Expression` to embed `expr.Expression`.
- Add `String()` method to `Abstraction`, `Application`, and `Variable` types.
- Update plugins to use `String()` instead of `lambda.Stringify()`.
### Decisions
- The `expr.Expression` interface is minimal (only `String()`) to avoid over-constraining future expression types.
- The engine still stores `*lambda.Expression` directly rather than `expr.Expression`, because Go's interface semantics require pointer indirection for in-place mutation during reduction.
- Future evaluation modes will implement their own concrete types satisfying `expr.Expression`.
## Benefits
- Establishes a foundation for supporting multiple evaluation modes (SKI combinators, typed lambda calculus, etc.).
- Plugins now use the abstract `String()` method, making them more decoupled from the lambda-specific implementation.
- Prepares the codebase for a Reducer interface abstraction in a future PR.
## Checklist
- [x] Code follows conventional commit format.
- [x] Branch follows naming convention (`<type>/<description>`).
- [x] Tests pass (if applicable).
- [ ] Documentation updated (if applicable).
Reviewed-on: #30
Co-authored-by: M.V. Hutz <git@maximhutz.me>
Co-committed-by: M.V. Hutz <git@maximhutz.me>
## Description
This PR refactors the event emitter system from a string-based message passing approach to a type-safe generic implementation using typed events.
The previous system relied on string message names which were error-prone and lacked compile-time safety.
This refactoring introduces a generic `BaseEmitter[E comparable]` that provides type safety while consolidating the various tracker packages into a unified plugins architecture.
Key changes:
- Replace `Emitter` with generic `BaseEmitter[E comparable]` for type-safe event handling.
- Add `Event` type enumeration with `StartEvent`, `StepEvent`, and `StopEvent` constants.
- Create `Listener[E]` interface with `BaseListener` implementation for better abstraction.
- Consolidate `explanation`, `performance`, and `statistics` packages into unified `internal/plugins` package.
- Simplify CLI initialization by using plugin constructors that handle their own event subscriptions.
- Add `Items()` iterator method to `Set` for idiomatic Go 1.23+ range loops over sets.
### Decisions
Use generics for type-safe event handling.
This provides compile-time guarantees that event types match their handlers while maintaining flexibility for future event types.
Consolidate trackers into plugins architecture.
Previously separate packages (`explanation`, `performance`, `statistics`) now live under `internal/plugins`, making the plugin pattern explicit and easier to extend.
Plugin constructors self-register with engine.
Each plugin's `New*` constructor now handles its own event subscriptions, reducing boilerplate in the main CLI.
## Benefits
Type safety prevents runtime errors from typos in event names.
The compiler now catches mismatched event types at compile time rather than failing silently at runtime.
Cleaner plugin architecture makes adding new features easier.
New plugins follow a consistent pattern and live in a single location.
Reduced boilerplate in main CLI.
Plugin initialization is now a single function call rather than manual event registration.
Better testability through interface-based design.
The `Listener[E]` interface allows for easier mocking and testing of event handlers.
## Checklist
- [x] Code follows conventional commit format.
- [x] Branch follows naming convention (`<type>/<description>`).
- [x] Tests pass (if applicable).
- [x] Documentation updated (if applicable).
Reviewed-on: #28
Co-authored-by: M.V. Hutz <git@maximhutz.me>
Co-committed-by: M.V. Hutz <git@maximhutz.me>