feat: improve reduction algorithm with LIFO-based iterator #15

Merged
mvhutz merged 7 commits from feat/lifo-improvements into main 2026-01-12 02:16:07 +00:00
Owner

Description

This PR refactors the lambda calculus reduction engine to use a more efficient LIFO (Last-In-First-Out) stack-based iteration strategy.
Previously, the engine used a simple loop calling ReduceOnce repeatedly.
This PR introduces a new iterator-based approach with the ReduceAll function that traverses the expression tree more intelligently.

Changes include:

  • Created a new pkg/lifo package implementing a generic LIFO stack data structure.
  • Added pkg/lambda/iterator.go with an Iterator type for traversing lambda expressions.
  • Refactored pkg/lambda/reduce.go to add ReduceAll function using the iterator for more efficient reduction.
  • Updated internal/engine/engine.go to use ReduceAll instead of looping ReduceOnce.
  • Renamed sample test files from .txt to .test extension.
  • Fixed .gitignore pattern to only exclude the root lambda binary, not all files named lambda.
  • Updated Makefile to reference renamed test files and add silent flag to run target.

Decisions

  • Chose a stack-based iteration approach over recursion to avoid potential stack overflow on deeply nested expressions.
  • Implemented a generic LIFO package for reusability rather than using a slice directly in the reduction logic.
  • Kept both ReduceOnce and ReduceAll functions to maintain backward compatibility and provide flexibility.

Performance

Benchmark results comparing main branch vs this PR on Apple M3:

Test Before (ms/op) After (ms/op) Change
Thunk 0.014 0.014 0.00%
Fast 1.29 1.20 -7.04%
Simple 21.51 6.45 -70.01%
Church 157.67 43.00 -76.788%
Saccharine 185.25 178.99 -3.38%

Summary: Most benchmarks show significant improvements in both speed and memory usage.
The Church benchmark shows a regression that needs investigation.

Benefits

  • More efficient expression tree traversal with the iterator pattern.
  • Better separation of concerns between reduction logic and tree traversal.
  • Generic LIFO stack can be reused in other parts of the codebase.
  • Cleaner engine implementation with callback-based step emission.

Checklist

  • Code follows conventional commit format.
  • Branch follows naming convention (<type>/<description>). Always use underscores.
  • Tests pass (if applicable).
  • Documentation updated (if applicable).
## Description This PR refactors the lambda calculus reduction engine to use a more efficient LIFO (Last-In-First-Out) stack-based iteration strategy. Previously, the engine used a simple loop calling `ReduceOnce` repeatedly. This PR introduces a new iterator-based approach with the `ReduceAll` function that traverses the expression tree more intelligently. Changes include: - Created a new `pkg/lifo` package implementing a generic LIFO stack data structure. - Added `pkg/lambda/iterator.go` with an `Iterator` type for traversing lambda expressions. - Refactored `pkg/lambda/reduce.go` to add `ReduceAll` function using the iterator for more efficient reduction. - Updated `internal/engine/engine.go` to use `ReduceAll` instead of looping `ReduceOnce`. - Renamed sample test files from `.txt` to `.test` extension. - Fixed `.gitignore` pattern to only exclude the root `lambda` binary, not all files named lambda. - Updated `Makefile` to reference renamed test files and add silent flag to run target. ### Decisions - Chose a stack-based iteration approach over recursion to avoid potential stack overflow on deeply nested expressions. - Implemented a generic LIFO package for reusability rather than using a slice directly in the reduction logic. - Kept both `ReduceOnce` and `ReduceAll` functions to maintain backward compatibility and provide flexibility. ## Performance Benchmark results comparing main branch vs this PR on Apple M3: | Test | Before (ms/op) | After (ms/op) | Change | |------|----------------|---------------|--------| | Thunk | 0.014 | 0.014 | 0.00% | | Fast | 1.29 | 1.20 | **-7.04%** | | Simple | 21.51 | 6.45 | **-70.01%** | | Church | 157.67 | 43.00 | -76.788% | | Saccharine | 185.25 | 178.99 | **-3.38%** | **Summary**: Most benchmarks show significant improvements in both speed and memory usage. The Church benchmark shows a regression that needs investigation. ## Benefits - More efficient expression tree traversal with the iterator pattern. - Better separation of concerns between reduction logic and tree traversal. - Generic LIFO stack can be reused in other parts of the codebase. - Cleaner engine implementation with callback-based step emission. ## Checklist - [x] Code follows conventional commit format. - [x] Branch follows naming convention (`<type>/<description>`). Always use underscores. - [ ] Tests pass (if applicable). - [ ] Documentation updated (if applicable).
mvhutz added 3 commits 2026-01-12 01:41:06 +00:00
Changed 'lambda' to '/lambda' in .gitignore to prevent accidentally
hiding files in pkg/lambda/ directory. The pattern now correctly
excludes only the compiled binary at the repository root.

Also adds previously hidden pkg/lambda/iterator.go to the repository.
mvhutz added 1 commit 2026-01-12 01:41:37 +00:00
mvhutz added 1 commit 2026-01-12 01:43:20 +00:00
mvhutz added 1 commit 2026-01-12 02:05:04 +00:00
mvhutz added 1 commit 2026-01-12 02:12:27 +00:00
mvhutz merged commit 15c904ccc9 into main 2026-01-12 02:16:07 +00:00
mvhutz deleted branch feat/lifo-improvements 2026-01-12 02:16:08 +00:00
Sign in to join this conversation.