Skip to content
This repository was archived by the owner on Apr 29, 2026. It is now read-only.

error stdlib enhancement#74

Merged
navicore merged 2 commits intomainfrom
error-prop
Dec 10, 2025
Merged

error stdlib enhancement#74
navicore merged 2 commits intomainfrom
error-prop

Conversation

@navicore
Copy link
Copy Markdown
Owner

⏺ Phase 1 & 2 are complete. Here's what was implemented:

see #73 for requirements

std:result module provides:

Result helpers (work with any Result-like union where Ok=tag 0, Err=tag 1):

  • result-ok? / result-err? - predicates
  • result-unwrap / result-unwrap-err - extract values (panic on wrong variant)
  • result-unwrap-or - extract with default
  • result-bind - chain fallible operations (point-free!)
  • result-map-with - transform Ok value

Option helpers (work with any Option-like union where Some=tag 0, None=tag 1):

  • option-some? / option-none? - predicates
  • option-unwrap - extract value (panic on None)
  • option-unwrap-or - extract with default
  • option-bind - chain optional operations (point-free!)
  • option-map-with - transform Some value

Key design decision: Since Seq doesn't have generics, users define their own Result/Option unions with specific types:

include std:result

union IntResult { Ok { value: Int } Err { error: String } }

: safe-divide ( Int Int -- IntResult )
dup 0 = if drop "Division by zero" Make-Err else divide Make-Ok then ;

Point-free chaining:

input [ parse ] result-bind [ validate ] result-bind [ process ] result-bind

This approach respects Seq's type system while providing ergonomic error handling.

⏺ Phase 1 & 2 are complete. Here's what was implemented:

  std:result module provides:

  Result helpers (work with any Result-like union where Ok=tag 0, Err=tag 1):
  - result-ok? / result-err? - predicates
  - result-unwrap / result-unwrap-err - extract values (panic on wrong variant)
  - result-unwrap-or - extract with default
  - result-bind - chain fallible operations (point-free!)
  - result-map-with - transform Ok value

  Option helpers (work with any Option-like union where Some=tag 0, None=tag 1):
  - option-some? / option-none? - predicates
  - option-unwrap - extract value (panic on None)
  - option-unwrap-or - extract with default
  - option-bind - chain optional operations (point-free!)
  - option-map-with - transform Some value

  Key design decision: Since Seq doesn't have generics, users define their own Result/Option unions with specific types:

  include std:result

  union IntResult { Ok { value: Int } Err { error: String } }

  : safe-divide ( Int Int -- IntResult )
    dup 0 = if
      drop "Division by zero" Make-Err
    else
      divide Make-Ok
    then
  ;

  # Point-free chaining:
  input [ parse ] result-bind [ validate ] result-bind [ process ] result-bind

  This approach respects Seq's type system while providing ergonomic error handling.
@claude
Copy link
Copy Markdown

claude Bot commented Dec 10, 2025

PR Review: Error stdlib enhancement

Great work on implementing Result/Option helpers for Seq! This is a well-thought-out implementation that addresses the requirements from issue #73. Below is my detailed review:


✅ Strengths

Code Quality & Design

  • Excellent documentation: The module header provides clear usage examples and conventions. This matches the style of other stdlib modules like json.seq and stack-utils.seq.
  • Consistent naming: Function names follow a clear pattern (result-*, option-*) making the API intuitive.
  • Stack effect comments: All functions have proper stack effect annotations, essential for a stack-based language.
  • Non-destructive predicates: The *-ok?, *-err?, *-some?, *-none? predicates use dup to keep the value on the stack, following Forth/stack language conventions.
  • Point-free style support: The result-bind and option-bind combinators enable elegant functional composition without explicit error checking at each step.

Implementation Correctness

  • Proper variant tag checks: All functions correctly check variant tags (0 for Ok/Some, 1 for Err/None).
  • Safe unwrapping: The unwrap functions provide clear panic messages when used incorrectly.
  • Correct stack manipulation: The stack shuffling in combinators (rot, swap, nip) appears correct.

Test Coverage

The test file (test_result.seq) is comprehensive:

  • ✅ Tests all predicates (Ok/Err/Some/None)
  • ✅ Tests unwrapping with both success and error cases
  • ✅ Tests combinators (bind with Ok→Ok, Ok→Err, Err passthrough)
  • ✅ Tests chaining multiple operations
  • ✅ Clear pass/fail reporting

🔍 Issues & Suggestions

1. Missing result-map and option-map (Mentioned in Docs)

Issue: The header documentation at lines 73-76 lists:

# Combinators:
#   - result-map: ( Result Quotation -- Result ) - Transform Ok value
#   - option-map: ( Option Quotation -- Option ) - Transform Some value

However, the actual implementations are result-map-with and option-map-with which require an additional constructor parameter.

Location:

  • Documentation: crates/compiler/stdlib/result.seq:73-76
  • Implementation: crates/compiler/stdlib/result.seq:149-157 (result-map-with), 216-224 (option-map-with)

Recommendation: Either:

  1. Update the documentation to reflect *-map-with as the actual function names, OR
  2. Add simpler result-map and option-map wrappers that use 0 make-variant-1 to construct the result (assuming tag 0, 1 field)

Example fix (option 2):

# Simple map - assumes Ok/Some is tag 0 with 1 field
: result-map ( Variant Quotation -- Variant )
  [ 0 make-variant-1 ] result-map-with
;

: option-map ( Variant Quotation -- Variant )
  [ 0 make-variant-1 ] option-map-with
;

2. Panic Implementation is Unsafe

Issue: The result-panic function creates an infinite recursion:

: result-panic ( -- )
  result-panic
;

Location: crates/compiler/stdlib/result.seq:133-135

Problem: While this does halt execution, it will:

  • Overflow the call stack
  • Provide no useful debugging information beyond the initial write_line
  • May behave differently depending on compiler optimizations

Recommendation:

  • If Seq has a built-in panic/abort mechanism, use that instead
  • If not, consider using an infinite loop that doesn't consume call stack:
: result-panic-loop ( -- )
  result-panic-loop
;

: result-panic ( -- )
  result-panic-loop
;
  • Or use a halt instruction if available

3. Inconsistent Stack Effect Types in Comments

Issue: Most stack effect comments use specific types like Variant, but some are overly generic.

Examples:

  • Line 90: : result-ok? ( Variant -- Variant Int ) ✅ Good
  • Line 102: : result-unwrap ( Variant -- Variant ) ⚠️ Misleading - the output type depends on what's in the variant, not necessarily Variant

Recommendation: For unwrap functions, use a more descriptive comment:

: result-unwrap ( Variant -- value )  # value type depends on Result definition

This is partially addressed with the shorter stack comments on lines 100-101, but the formal stack effect should also clarify.


4. Missing Helper: result-map-err

Suggestion: Consider adding result-map-err to transform error values while passing through Ok. This is useful for:

  • Converting error types
  • Adding context to errors
  • Normalizing errors from different sources

Example:

: result-map-err ( Result Quotation ErrConstructor -- Result )
  rot result-err? if
    result-unwrap-err
    rot call
    swap call
  else
    rot drop rot drop
  then
;

Usage:

my-result [ "Context: " swap string-concat ] [ Make-Err ] result-map-err

5. Test Coverage - Missing Unwrap-Or Tests

Issue: The test file doesn't test result-unwrap-or or option-unwrap-or.

Location: crates/compiler/stdlib/test_result.seq

Recommendation: Add tests like:

: test-unwrap-or-ok ( -- )
  42 Make-Ok 99
  result-unwrap-or 42 = if
    "unwrap-or with Ok" test-pass
  else
    "unwrap-or with Ok" test-fail
  then
;

: test-unwrap-or-err ( -- )
  "error" Make-Err 99
  result-unwrap-or 99 = if
    "unwrap-or with Err returns default" test-pass
  else
    "unwrap-or with Err" test-fail
  then
;

6. Documentation: Make-Ok / Make-Err Undefined

Issue: The examples use Make-Ok and Make-Err but these aren't defined in the module. Users need to define these themselves based on their union type.

Location: crates/compiler/stdlib/result.seq:29-31, 40

Recommendation: Add a note early in the documentation (perhaps around line 7) explicitly stating:

# Note: You must define your own union and use the generated Make-* constructors.
# The helpers in this module work with any union that follows the conventions.

🚀 Performance Considerations

Overall: Performance looks good. The implementation uses:

  • Direct variant-tag comparisons (O(1))
  • Minimal stack manipulation
  • No unnecessary allocations

Observation: The *-bind functions are efficient as they only call the quotation if the result is Ok/Some, avoiding unnecessary work.


🔒 Security Considerations

No security issues identified. The module:

  • Doesn't perform any unsafe operations
  • Doesn't interact with external systems
  • Provides proper error messages for misuse
  • Uses stack-safe operations

📊 Test Coverage Summary

Category Coverage Notes
Predicates ✅ Excellent All predicates tested
Unwrap ⚠️ Good Missing unwrap-or tests
Combinators ✅ Good bind tested, map-with not tested
Chaining ✅ Excellent Multi-step chains tested
Error cases ✅ Good Tests both success and failure paths

✅ Recommendations Summary

Must Fix (before merge):

  1. Fix documentation mismatch: result-map vs result-map-with
  2. Add tests for *-unwrap-or functions

Should Fix (high priority):
3. Improve panic implementation (avoid infinite recursion)
4. Clarify Make-* constructor requirements in docs

Nice to Have (future enhancement):
5. Add result-map-err combinator
6. Add option-map-err if needed
7. Test the *-map-with functions


🎯 Conclusion

This is a high-quality implementation that successfully addresses Phase 1 & 2 from issue #73. The code is well-documented, follows Seq conventions, and provides a solid foundation for error handling in Seq programs.

The issues identified are minor and mostly documentation-related. With the recommended fixes, this will be an excellent addition to the stdlib.

Approval: ✅ Approve with minor changes

Great work, @navicore! This will significantly improve error handling ergonomics in Seq.

⏺ All 28 tests pass. The PR review feedback has been addressed:

  1. Added result-map and option-map - simpler versions that use make-variant-1 instead of requiring a constructor quotation
  2. Added tests for *-unwrap-or - both Result and Option versions
  3. Added tests for *-map - both Result and Option versions
  4. Fixed type signatures - used different type variables (Result Default -- Default) to properly express the relationship

  The panic implementation is fine as-is because Seq has TCO - result-panic calling itself is compiled to an infinite loop (jump), not stack-consuming
  recursion.
@claude
Copy link
Copy Markdown

claude Bot commented Dec 10, 2025

Pull Request Review: Error stdlib enhancement

Overall Assessment

Status: ✅ Approved with minor suggestions

This is an excellent implementation of Result/Option helpers for Seq. The code is well-documented, follows consistent patterns, and provides comprehensive test coverage. The design decision to use tag-based conventions rather than generics is pragmatic given Seq's type system.


Code Quality & Best Practices ⭐ Excellent

Strengths:

  • Comprehensive documentation: The header comments in result.seq:1-81 provide clear usage examples, conventions, and point-free chaining patterns
  • Consistent naming: All functions follow clear naming conventions (result-*, option-*, -unwrap, -unwrap-or, -map, -bind)
  • Stack comments: Proper stack effect annotations throughout (e.g., ( Variant -- Variant Int ))
  • Modular organization: Well-organized sections with clear separators in result.seq:83-265
  • Follows stdlib patterns: Matches the style and structure of existing stdlib modules like math.seq and stack-utils.seq

Minor suggestions:

  1. Type annotations consistency: The stack comments use both generic names (Variant, Result, Option) and specific types (Default). Consider standardizing - perhaps:

    # Current:
    : result-unwrap-or ( Result Default -- Default )
    
    # Suggestion:
    : result-unwrap-or ( Variant Default -- Default )
    

    This makes it clearer these work with any Result-like union.

  2. Documentation example alignment: In result.seq:41, the main function example could benefit from demonstrating the point-free chaining mentioned in the intro:

    # Add example showing:
    input [ parse ] result-bind [ validate ] result-bind [ process ] result-bind
    

Potential Bugs or Issues ✅ None Found

Analysis:

  • Panic implementation (result.seq:133-135): The infinite recursive call is intentional and correctly relies on Seq's TCO. As noted in commit message, this compiles to an infinite loop (jump) rather than stack-consuming recursion. This is valid.
  • Variant tag assumptions: The convention that Ok/Some = tag 0, Err/None = tag 1 is clearly documented in result.seq:48-55. This is enforced by user-defined unions, not the helpers themselves.
  • Stack safety: All stack operations are balanced and properly annotated.
  • Edge cases: Unwrap functions correctly panic on wrong variants with clear error messages (result.seq:105-107, result.seq:114-116, result.seq:203-205)

No bugs identified.


Performance Considerations ⚡ Efficient

Strengths:

  • Point-free combinators: result-bind and option-bind enable efficient function composition without intermediate allocations
  • Simple map helpers: The addition of result-map and option-map using make-variant-1 avoids quotation overhead compared to -map-with variants
  • Minimal stack manipulation: Operations use efficient stack words (nip, rot, swap) rather than expensive operations

Observations:

  • Variant operations: Uses low-level variant-tag and variant-field-at which should be efficient primitive operations
  • No allocations: Helpers don't create unnecessary intermediate values
  • TCO-friendly: The panic implementation respects tail call optimization

No performance concerns.


Security Concerns 🔒 None

Analysis:

  • Panic safety: Panic messages are hardcoded strings with no user input interpolation
  • No unsafe operations: All operations use Seq's safe primitives
  • No external I/O: This is a pure library module
  • Type safety: Relies on Seq's type system for variant correctness

No security vulnerabilities identified.


Test Coverage 🧪 Excellent

Strengths:

  • Comprehensive test suite: test_result.seq contains 28 tests covering all major functionality
  • Clear test organization: Tests grouped by category with section headers (test_result.seq:387-427)
  • Both paths tested: Tests cover both success and failure cases for predicates
  • Combinator coverage: Includes tests for bind operations with Ok→Ok, Ok→Err, and Err passthrough (test_result.seq:136-164)
  • Unwrap-or tests: Added in response to PR review feedback (test_result.seq:93-112, test_result.seq:237-258)
  • Map tests: Both simple *-map and custom *-map-with variants are tested
  • Integration tests: Chaining tests demonstrate real-world usage (test_result.seq:331-373)

Test coverage breakdown:

  • ✅ Result predicates: result-ok?, result-err?
  • ✅ Option predicates: option-some?, option-none?
  • ✅ Result unwrapping: result-unwrap, result-unwrap-err, result-unwrap-or
  • ✅ Option unwrapping: option-unwrap, option-unwrap-or
  • ✅ Result combinators: result-bind, result-map
  • ✅ Option combinators: option-bind, option-map
  • ✅ Chaining: Multi-step result-bind chains with early error propagation

Excellent test coverage. All major code paths are tested.


Alignment with Issue #73

The PR successfully implements Phase 1 & 2 from issue #73:

Phase 1: Result/Option helpers with predicates, unwrap, unwrap-or
Phase 2: Combinators (result-map, result-bind, option-map, option-bind)
⏸️ Phase 3: Early return / try? semantics (deferred, requires compiler support)

The implementation correctly handles Seq's lack of generics by using tag-based conventions, which is the right pragmatic choice.


Minor Observations

  1. Embed registration (stdlib_embed.rs:17): Correctly adds the new module to the stdlib map
  2. Commit messages: Well-detailed with clear explanations of changes
  3. Response to feedback: The second commit shows excellent responsiveness to PR review feedback

Recommendations

For merge:

  • Ready to merge - This is high-quality code that significantly improves Seq's error handling story

For future consideration:

  1. Add examples in examples/ directory showing real-world Result/Option usage (e.g., file I/O with error handling)
  2. Consider documenting the Result/Option pattern in the language guide
  3. When Phase 3 (try?) is explored, ensure it integrates cleanly with these helpers

Summary

This PR delivers exactly what was promised: ergonomic, well-tested Result/Option helpers that respect Seq's type system and concatenative paradigm. The code quality is excellent, documentation is thorough, and test coverage is comprehensive.

Verdict: LGTM! 🚀

Great work, @navicore!


🤖 Generated with Claude Code

Co-Reviewed-By: Claude Sonnet 4.5 noreply@anthropic.com

@navicore navicore merged commit 8052f82 into main Dec 10, 2025
2 checks passed
@navicore navicore deleted the error-prop branch December 10, 2025 03:46
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant