Issue 131 #138

Merged
navicore merged 7 commits from issue-131 into main 2025-12-30 15:46:15 +00:00
navicore commented 2025-12-30 03:30:30 +00:00 (Migrated from github.com)
https://github.com/navicore/patch-seq/issues/131
claude[bot] commented 2025-12-30 03:32:44 +00:00 (Migrated from github.com)

Code Review: PR #138 - Generator/Weave Support

Overview

This PR implements generator/coroutine support for Seq through a new "weave" system. The implementation adds strand.weave, strand.resume, and yield operations to enable lazy sequences and backtracking patterns (particularly for the seq-prolog project).

Strengths

1. Excellent Architecture & Documentation

  • The weave.rs module has comprehensive documentation explaining the architecture, flow, and API
  • Clear explanation of the bidirectional channel pattern (yield_chan/resume_chan)
  • Well-documented sentinel pattern for completion signaling

2. Consistent Naming Convention

  • Successfully migrated from spawn to strand.spawn throughout the codebase
  • New operations follow the established strand. namespace convention
  • Good consistency across 15 files including benchmarks and examples

3. Type System Integration

  • Type signatures properly defined in builtins.rs
  • Polymorphic stack effects using row variables (RowVar)
  • Type checker integration appears sound

4. Follows Existing Patterns

  • Weave implementation mirrors the existing spawn implementation patterns
  • Reuses ACTIVE_STRANDS counter and shutdown synchronization
  • Consistent with May coroutine usage patterns

⚠️ Issues & Concerns

1. CRITICAL: Missing Test Coverage

Severity: High

The PR adds 434 lines of new runtime code but includes only a single example program (counter.seq) with no automated tests.

Required:

  • Integration tests in tests/integration/src/test-weave.seq
  • Test cases should cover:
    • Basic yield/resume flow
    • Multiple yields in a loop
    • Weave completion detection
    • Invalid weave ID handling
    • Calling yield outside a weave (should panic)
    • Resuming a completed weave
    • Type polymorphism (yield with different value types)
    • Memory safety with arena strings

Reference: See existing test patterns in tests/integration/src/test-channel-safe.seq

2. CRITICAL: Binary File in PR

Severity: High

The file http_simple (2.4MB executable) was accidentally committed:

{"path":"http_simple","additions":0,"deletions":0}

Action Required:

git rm http_simple
git commit --amend
git push --force-with-lease

Add to .gitignore if not already present:

# Compiled executables
/http_simple
/examples/**/\!(*.seq|*.md)

3. Potential Memory Leak in Weave Registry

Severity: Medium

Location: crates/runtime/src/weave.rs:352-357

If a weave is created but never resumed to completion, the entry remains in WEAVE_REGISTRY indefinitely.

Scenario:

[ counter-body ] strand.weave  # Creates weave ID 1
drop                            # Discard weave ID without completing
# Registry still holds channels for weave ID 1

Suggested Fix:
Consider adding a cleanup mechanism:

  • Weak references or timeout-based cleanup
  • Explicit strand.weave-close operation
  • Document that weaves must be run to completion or the behavior is undefined

4. DONE_SENTINEL Collision Risk

Severity: Low

Location: crates/runtime/src/weave.rs:39

const DONE_SENTINEL: i64 = i64::MIN;

While unlikely, a weave could legitimately yield i64::MIN, causing premature completion detection.

Suggested Fix:
Use a dedicated enum or Option<Value> in the channel:

enum WeaveMessage {
    Yielded(Value),
    Done,
}

5. Panic on Invalid Weave ID

Severity: Low

Location: crates/runtime/src/weave.rs:309-315

Resuming with a non-Int value panics. Consider if this should return an error state instead for better error handling.

Current:

let weave_id = match weave_id_val {
    Value::Int(id) => id,
    _ => panic\!("..."),
};

Consider: Return (weave_id, 0, false) to indicate error without panic.

6. Thread-Local Storage with May Migration

Severity: Low

Location: crates/runtime/src/weave.rs:55-56

The CURRENT_WEAVE uses thread-local storage, but May can migrate coroutines between threads. While the architecture doc mentions this is acceptable for arenas, verify that:

  • A weave's context is correctly re-established if May migrates it
  • Or document that weaves must not be migrated (pin to thread)

7. Type Signature Limitation

Severity: Low

Location: crates/compiler/src/builtins.rs:495-503

The yield type signature is ( a b -- a b ), which implies the yielded and resumed values must be the same type. This may be too restrictive.

Example that might not type-check:

: example
  42 yield      # yield Int
  drop
  "hello" yield  # yield String
;

Consider if this should be ( a b -- a c ) to allow different resume types.

📋 Minor Issues

8. Code Duplication in weave.rs

Lines 124-200 (Quotation handling) and 201-261 (Closure handling) have significant duplication. Consider extracting common logic:

fn spawn_weave_coroutine(
    stack_setup: impl FnOnce() -> Stack,
    executor: impl FnOnce(Stack) -> Stack,
    yc: Arc<ChannelData>,
    rc: Arc<ChannelData>,
) { /* ... */ }

9. Incomplete Naming Migration

While most references were updated to strand.spawn, verify that:

  • All documentation files mention the new naming
  • Any error messages reference strand.spawn not spawn
  • REPL help text is updated

10. Missing Documentation Updates

The following docs should be updated:

  • docs/ARCHITECTURE.md:213-228 - Update concurrency section to mention weaves
  • docs/language-guide.md - Add weave/generator examples
  • README.md:122-125 - Update concurrency section

🔒 Security Assessment

No security issues identified

  • No SQL injection, XSS, or command injection vectors
  • Memory safety maintained through Rust's type system
  • Arena string cloning prevents use-after-free
  • Channel-based communication is safe

🎯 Performance Considerations

Positive:

  • Reuses efficient May channel primitives
  • No additional allocations beyond channels
  • Atomic operations for ID generation

Concerns:

  • Each weave creates two channels (some overhead)
  • Global WEAVE_REGISTRY lock on every resume (potential contention)
  • Consider lock-free registry (e.g., DashMap) if high concurrency expected

Action Items

Must Fix (Blocking):

  1. Remove http_simple binary file
  2. Add integration tests for weave functionality
  3. Document or fix weave registry leak scenario

Should Fix:
4. ⚠️ Replace DONE_SENTINEL with proper enum
5. ⚠️ Document thread migration behavior with weaves
6. ⚠️ Update architecture documentation

Nice to Have:
7. Reduce code duplication in weave.rs
8. Consider more flexible yield type signature
9. Add docs/WEAVE_GUIDE.md with detailed examples

📊 Overall Assessment

Code Quality: B+ (well-structured, good patterns, missing tests)
Documentation: B (good inline docs, needs user-facing guide)
Completeness: C+ (core implementation solid, testing incomplete)

Recommendation: Request changes for:

  1. Binary file removal (critical)
  2. Test coverage (critical)
  3. Registry cleanup documentation (important)

Once addressed, this will be a solid addition to the language. The architecture is sound and follows established patterns well.


Review generated with assistance from Claude Code

# Code Review: PR #138 - Generator/Weave Support ## Overview This PR implements generator/coroutine support for Seq through a new "weave" system. The implementation adds `strand.weave`, `strand.resume`, and `yield` operations to enable lazy sequences and backtracking patterns (particularly for the seq-prolog project). ## ✅ Strengths ### 1. **Excellent Architecture & Documentation** - The `weave.rs` module has comprehensive documentation explaining the architecture, flow, and API - Clear explanation of the bidirectional channel pattern (yield_chan/resume_chan) - Well-documented sentinel pattern for completion signaling ### 2. **Consistent Naming Convention** - Successfully migrated from `spawn` to `strand.spawn` throughout the codebase - New operations follow the established `strand.` namespace convention - Good consistency across 15 files including benchmarks and examples ### 3. **Type System Integration** - Type signatures properly defined in `builtins.rs` - Polymorphic stack effects using row variables (`RowVar`) - Type checker integration appears sound ### 4. **Follows Existing Patterns** - Weave implementation mirrors the existing `spawn` implementation patterns - Reuses `ACTIVE_STRANDS` counter and shutdown synchronization - Consistent with May coroutine usage patterns ## ⚠️ Issues & Concerns ### 1. **CRITICAL: Missing Test Coverage** **Severity: High** The PR adds 434 lines of new runtime code but includes only a single example program (`counter.seq`) with no automated tests. **Required:** - Integration tests in `tests/integration/src/test-weave.seq` - Test cases should cover: - Basic yield/resume flow - Multiple yields in a loop - Weave completion detection - Invalid weave ID handling - Calling `yield` outside a weave (should panic) - Resuming a completed weave - Type polymorphism (`yield` with different value types) - Memory safety with arena strings **Reference:** See existing test patterns in `tests/integration/src/test-channel-safe.seq` ### 2. **CRITICAL: Binary File in PR** **Severity: High** The file `http_simple` (2.4MB executable) was accidentally committed: ``` {"path":"http_simple","additions":0,"deletions":0} ``` **Action Required:** ```bash git rm http_simple git commit --amend git push --force-with-lease ``` Add to `.gitignore` if not already present: ``` # Compiled executables /http_simple /examples/**/\!(*.seq|*.md) ``` ### 3. **Potential Memory Leak in Weave Registry** **Severity: Medium** **Location:** `crates/runtime/src/weave.rs:352-357` If a weave is created but never resumed to completion, the entry remains in `WEAVE_REGISTRY` indefinitely. **Scenario:** ```seq [ counter-body ] strand.weave # Creates weave ID 1 drop # Discard weave ID without completing # Registry still holds channels for weave ID 1 ``` **Suggested Fix:** Consider adding a cleanup mechanism: - Weak references or timeout-based cleanup - Explicit `strand.weave-close` operation - Document that weaves must be run to completion or the behavior is undefined ### 4. **DONE_SENTINEL Collision Risk** **Severity: Low** **Location:** `crates/runtime/src/weave.rs:39` ```rust const DONE_SENTINEL: i64 = i64::MIN; ``` While unlikely, a weave could legitimately yield `i64::MIN`, causing premature completion detection. **Suggested Fix:** Use a dedicated enum or `Option<Value>` in the channel: ```rust enum WeaveMessage { Yielded(Value), Done, } ``` ### 5. **Panic on Invalid Weave ID** **Severity: Low** **Location:** `crates/runtime/src/weave.rs:309-315` Resuming with a non-Int value panics. Consider if this should return an error state instead for better error handling. **Current:** ```rust let weave_id = match weave_id_val { Value::Int(id) => id, _ => panic\!("..."), }; ``` **Consider:** Return `(weave_id, 0, false)` to indicate error without panic. ### 6. **Thread-Local Storage with May Migration** **Severity: Low** **Location:** `crates/runtime/src/weave.rs:55-56` The `CURRENT_WEAVE` uses thread-local storage, but May can migrate coroutines between threads. While the architecture doc mentions this is acceptable for arenas, verify that: - A weave's context is correctly re-established if May migrates it - Or document that weaves must not be migrated (pin to thread) ### 7. **Type Signature Limitation** **Severity: Low** **Location:** `crates/compiler/src/builtins.rs:495-503` The `yield` type signature is `( a b -- a b )`, which implies the yielded and resumed values must be the same type. This may be too restrictive. **Example that might not type-check:** ```seq : example 42 yield # yield Int drop "hello" yield # yield String ; ``` Consider if this should be `( a b -- a c )` to allow different resume types. ## 📋 Minor Issues ### 8. **Code Duplication in weave.rs** Lines 124-200 (Quotation handling) and 201-261 (Closure handling) have significant duplication. Consider extracting common logic: ```rust fn spawn_weave_coroutine( stack_setup: impl FnOnce() -> Stack, executor: impl FnOnce(Stack) -> Stack, yc: Arc<ChannelData>, rc: Arc<ChannelData>, ) { /* ... */ } ``` ### 9. **Incomplete Naming Migration** While most references were updated to `strand.spawn`, verify that: - All documentation files mention the new naming - Any error messages reference `strand.spawn` not `spawn` - REPL help text is updated ### 10. **Missing Documentation Updates** The following docs should be updated: - `docs/ARCHITECTURE.md:213-228` - Update concurrency section to mention weaves - `docs/language-guide.md` - Add weave/generator examples - `README.md:122-125` - Update concurrency section ## 🔒 Security Assessment ✅ **No security issues identified** - No SQL injection, XSS, or command injection vectors - Memory safety maintained through Rust's type system - Arena string cloning prevents use-after-free - Channel-based communication is safe ## 🎯 Performance Considerations ### Positive: - Reuses efficient May channel primitives - No additional allocations beyond channels - Atomic operations for ID generation ### Concerns: - Each weave creates two channels (some overhead) - Global `WEAVE_REGISTRY` lock on every resume (potential contention) - Consider lock-free registry (e.g., `DashMap`) if high concurrency expected ## ✅ Action Items **Must Fix (Blocking):** 1. ❌ Remove `http_simple` binary file 2. ❌ Add integration tests for weave functionality 3. ❌ Document or fix weave registry leak scenario **Should Fix:** 4. ⚠️ Replace `DONE_SENTINEL` with proper enum 5. ⚠️ Document thread migration behavior with weaves 6. ⚠️ Update architecture documentation **Nice to Have:** 7. ✨ Reduce code duplication in `weave.rs` 8. ✨ Consider more flexible `yield` type signature 9. ✨ Add `docs/WEAVE_GUIDE.md` with detailed examples ## 📊 Overall Assessment **Code Quality:** B+ (well-structured, good patterns, missing tests) **Documentation:** B (good inline docs, needs user-facing guide) **Completeness:** C+ (core implementation solid, testing incomplete) **Recommendation:** Request changes for: 1. Binary file removal (critical) 2. Test coverage (critical) 3. Registry cleanup documentation (important) Once addressed, this will be a solid addition to the language. The architecture is sound and follows established patterns well. --- *Review generated with assistance from Claude Code*
claude[bot] commented 2025-12-30 04:22:23 +00:00 (Migrated from github.com)

Code Review: PR #138 - Weave/Generator Implementation

Summary

This PR successfully implements generator/coroutine support (weaves) for Seq, addressing issue #131. The implementation adds three new operations (strand.weave, strand.resume, yield) with a zero-mutex, channel-based architecture that maintains consistency with the existing strand/channel design.

Overall Assessment: Strong implementation with excellent architecture. A few minor issues to address.


Strengths

1. Architectural Excellence

The zero-mutex design is outstanding:

  • No global registry - WeaveCtx travels on the stack like channels
  • Lock-free communication via MPMC channels
  • Consistent with existing channel/strand patterns
  • Clear separation: WeaveHandle for caller, WeaveCtx for weave body

2. Type System Integration

Well-integrated with Seq's type system:

  • Stack effects properly defined in builtins.rs
  • Row polymorphism preserved
  • Type-driven context threading documented clearly

3. Documentation Quality

Excellent inline documentation:

  • Module-level documentation in weave.rs explains architecture
  • Clear examples in counter.seq and test-weave.seq
  • Comments explain the "why" not just the "what"

4. Consistent Naming

The rename from spawn to strand.spawn maintains namespace consistency and sets up clean API evolution.


Issues & Recommendations

Critical

1. Potential Memory Leak in patch_seq_weave (crates/runtime/src/weave.rs:134)

When the quotation completes, you pop the WeaveCtx but don't properly clean up the final stack:

// Line 134
let (_, ctx_value) = pop(final_stack);

The returned stack pointer is discarded. If there are other values on the stack, they leak.

Fix: Either assert the stack is empty, or properly clean up all remaining values:

let (remaining_stack, ctx_value) = pop(final_stack);
// Assert stack is clean or properly deallocate remaining_stack

Same issue at line 188 for the Closure path.

2. Inconsistent Error Handling (crates/runtime/src/weave.rs:261, 330)

patch_seq_resume returns false on channel errors (good), but patch_seq_yield panics (bad):

// Line 330 - panics
if yield_chan.sender.send(value_to_send).is_err() {
    panic!("yield: yield channel closed unexpectedly");
}

Recommendation: Either document why yield should panic (programming error?) or make it return a Result/Bool for consistency. The current design means weaves can't gracefully handle cancellation from the caller side.

High Priority

3. Missing Stack Validation (crates/runtime/src/weave.rs:240, 306)

Both patch_seq_resume and patch_seq_yield assert stack is not null but don't verify sufficient depth:

// Line 240
assert!(!stack.is_null(), "strand.resume: stack is empty");
// But then immediately pops TWICE without checking depth
let (stack, value) = unsafe { pop(stack) };
let (stack, handle) = unsafe { pop(stack) };

Fix: Either document that pop is safe to call on empty stack (returns sentinel?) or add proper depth checks.

4. Type System Limitation (crates/compiler/src/builtins.rs:496-507)

The yield signature uses generic type variables but doesn't enforce that WeaveCtx is actually a WeaveCtx:

StackType::RowVar("a".to_string())
    .push(Type::Var("ctx".to_string()))  // Any type accepted!
    .push(Type::Var("b".to_string()))

This means the type checker won't catch:

42 "not a context" yield  # Type checks but panics at runtime

Recommendation: Either add a WeaveCtx type to the type system, or document this as a known limitation.

Medium Priority

5. Sentinel Value Choice (crates/runtime/src/weave.rs:40)

const DONE_SENTINEL: i64 = i64::MIN;

While unlikely, this prevents i64::MIN from being a valid yielded value.

Alternatives:

  • Use a tagged enum internally (Value::WeaveDone)
  • Document this limitation clearly in API docs
  • Use Option on the channel

6. Missing Test Coverage

The test file test-weave.seq is good but missing:

  • Test for weave that yields different types (if supported)
  • Test for multiple concurrent weaves
  • Test for weave + regular strand interaction
  • Test for resource cleanup when handle is dropped early
  • Stress test (many yields/resumes)

7. Auto-Closure Conversion Comment (crates/compiler/src/typechecker.rs:843)

// Special handling for strand.spawn: auto-convert Quotation to Closure if needed
let adjusted_stack = if name == "strand.spawn" {

Should this also handle strand.weave? The comment mentions spawn but not weave. Either add weave to the condition or document why weave doesn't need it.

Low Priority (Enhancements)

8. Performance: Unnecessary Clones

Lines 257-258 in weave.rs:

let value_to_send = value.clone();
// ...
if resume_chan.sender.send(value_to_send).is_err() {

You clone the value even if send might fail. Consider:

if resume_chan.sender.send(value).is_err() {
    // Don't need value anymore

Though Arc clones are cheap, avoiding unnecessary work is better.

9. Missing Debug Trait Implementation

WeaveCtx in value.rs is not included in any Debug implementations. This makes debugging harder.

10. Serialization Error Message (crates/runtime/src/serialize.rs:193)

Value::WeaveCtx { .. } => Err(SerializeError::ChannelNotSerializable),

The error message says "ChannelNotSerializable" for a WeaveCtx. Consider adding a dedicated WeaveNotSerializable error for clarity.


Security Assessment

No security concerns identified.

The implementation:

  • Doesn't introduce unsafe memory access patterns beyond standard stack operations
  • Uses proper Arc reference counting
  • Channel operations are already vetted in the may crate
  • No new FFI boundaries or external data handling

Performance Considerations

Overall: Good performance characteristics

Pros:

  • Zero-mutex design eliminates lock contention
  • O(1) Arc clones for handle passing
  • Channel-backed communication reuses existing, optimized infrastructure

Potential concerns:

  • Each weave spawns a real may coroutine (not zero-cost)
  • Channel send/recv involves may's scheduler (context switch overhead)
  • For tight loops, function calls + TCO might be faster than yield/resume

Recommendation: Add benchmark comparing weave-based iteration vs recursive approaches for the examples (counter, fibonacci, etc.)


Code Quality & Style

Excellent overall. The code follows Rust best practices:

  • Clear module organization
  • Proper use of unsafe with safety comments
  • Consistent error handling patterns (mostly)
  • Good use of Arc vs Box
  • Tagged stack implementation is clean

Minor style notes:

  • Some functions exceed 100 lines (e.g., patch_seq_weave). Consider extracting helpers for Quotation vs Closure paths.
  • Consistent use of unsafe { ... } blocks

Testing

Based on the commit messages, you report:

  • 113 integration tests pass
  • All examples compile
  • No lint issues

Recommendations:

  1. Add tests for edge cases mentioned above
  2. Add a benchmark suite for weave operations
  3. Consider fuzz testing the yield/resume interaction

Documentation

Good but could be expanded:

What's missing:

  • Update to main ARCHITECTURE.md explaining weaves
  • No update to README.md mentioning the new feature
  • Language guide doesn't mention weaves (docs/language-guide.md?)
  • Migration guide for users?

API Design

The explicit context threading is elegant but verbose. Compare:

Current:

: counter-loop ( Ctx Int -- )
  tuck yield rot i.add counter-loop ;

Alternative (if implicit):

: counter-loop ( Int -- )
  dup yield i.add counter-loop ;

The explicit approach is more principled and avoids hidden state. Good choice for a systems language.

Question: Is the plan to add effect tracking (as mentioned in issue #131 comments) in a future PR? That would make the context requirement compile-time verifiable.


Recommendations Summary

Must Fix

  1. Fix potential memory leak in weave completion (line 134, 188)
  2. Document or fix yield panic behavior (line 330)
  3. Add stack depth validation (line 240, 306)

Should Fix

  1. Document type system limitation for WeaveCtx
  2. Add missing test coverage
  3. Clarify auto-closure conversion for weave

Nice to Have

  1. Optimize unnecessary clones
  2. Add Debug implementations
  3. Improve error messages
  4. Update architecture docs
  5. Add benchmarks

Conclusion

This is high-quality work that successfully implements a complex feature with excellent architecture. The zero-mutex design is particularly impressive and shows deep understanding of the existing concurrency model.

The issues identified are primarily:

  • Memory safety edge cases that need verification
  • Missing test coverage for edge cases
  • Documentation gaps

Once the critical issues are addressed, this is ready to merge.

Estimated effort to address critical issues: 1-2 hours

Great work! 🎉

# Code Review: PR #138 - Weave/Generator Implementation ## Summary This PR successfully implements generator/coroutine support (weaves) for Seq, addressing issue #131. The implementation adds three new operations (`strand.weave`, `strand.resume`, `yield`) with a zero-mutex, channel-based architecture that maintains consistency with the existing strand/channel design. **Overall Assessment: Strong implementation with excellent architecture. A few minor issues to address.** --- ## Strengths ### 1. **Architectural Excellence** The zero-mutex design is outstanding: - No global registry - WeaveCtx travels on the stack like channels - Lock-free communication via MPMC channels - Consistent with existing channel/strand patterns - Clear separation: WeaveHandle for caller, WeaveCtx for weave body ### 2. **Type System Integration** Well-integrated with Seq's type system: - Stack effects properly defined in `builtins.rs` - Row polymorphism preserved - Type-driven context threading documented clearly ### 3. **Documentation Quality** Excellent inline documentation: - Module-level documentation in `weave.rs` explains architecture - Clear examples in `counter.seq` and `test-weave.seq` - Comments explain the "why" not just the "what" ### 4. **Consistent Naming** The rename from `spawn` to `strand.spawn` maintains namespace consistency and sets up clean API evolution. --- ## Issues & Recommendations ### Critical #### 1. **Potential Memory Leak in `patch_seq_weave`** (crates/runtime/src/weave.rs:134) When the quotation completes, you pop the WeaveCtx but don't properly clean up the final stack: ```rust // Line 134 let (_, ctx_value) = pop(final_stack); ``` The returned stack pointer is discarded. If there are other values on the stack, they leak. **Fix**: Either assert the stack is empty, or properly clean up all remaining values: ```rust let (remaining_stack, ctx_value) = pop(final_stack); // Assert stack is clean or properly deallocate remaining_stack ``` Same issue at line 188 for the Closure path. #### 2. **Inconsistent Error Handling** (crates/runtime/src/weave.rs:261, 330) `patch_seq_resume` returns false on channel errors (good), but `patch_seq_yield` panics (bad): ```rust // Line 330 - panics if yield_chan.sender.send(value_to_send).is_err() { panic!("yield: yield channel closed unexpectedly"); } ``` **Recommendation**: Either document why yield should panic (programming error?) or make it return a Result/Bool for consistency. The current design means weaves can't gracefully handle cancellation from the caller side. ### High Priority #### 3. **Missing Stack Validation** (crates/runtime/src/weave.rs:240, 306) Both `patch_seq_resume` and `patch_seq_yield` assert stack is not null but don't verify sufficient depth: ```rust // Line 240 assert!(!stack.is_null(), "strand.resume: stack is empty"); // But then immediately pops TWICE without checking depth let (stack, value) = unsafe { pop(stack) }; let (stack, handle) = unsafe { pop(stack) }; ``` **Fix**: Either document that pop is safe to call on empty stack (returns sentinel?) or add proper depth checks. #### 4. **Type System Limitation** (crates/compiler/src/builtins.rs:496-507) The `yield` signature uses generic type variables but doesn't enforce that WeaveCtx is actually a WeaveCtx: ```rust StackType::RowVar("a".to_string()) .push(Type::Var("ctx".to_string())) // Any type accepted! .push(Type::Var("b".to_string())) ``` This means the type checker won't catch: ```seq 42 "not a context" yield # Type checks but panics at runtime ``` **Recommendation**: Either add a WeaveCtx type to the type system, or document this as a known limitation. ### Medium Priority #### 5. **Sentinel Value Choice** (crates/runtime/src/weave.rs:40) ```rust const DONE_SENTINEL: i64 = i64::MIN; ``` While unlikely, this prevents `i64::MIN` from being a valid yielded value. **Alternatives**: - Use a tagged enum internally (Value::WeaveDone) - Document this limitation clearly in API docs - Use Option<Value> on the channel #### 6. **Missing Test Coverage** The test file `test-weave.seq` is good but missing: - Test for weave that yields different types (if supported) - Test for multiple concurrent weaves - Test for weave + regular strand interaction - Test for resource cleanup when handle is dropped early - Stress test (many yields/resumes) #### 7. **Auto-Closure Conversion Comment** (crates/compiler/src/typechecker.rs:843) ```rust // Special handling for strand.spawn: auto-convert Quotation to Closure if needed let adjusted_stack = if name == "strand.spawn" { ``` Should this also handle `strand.weave`? The comment mentions spawn but not weave. Either add weave to the condition or document why weave doesn't need it. ### Low Priority (Enhancements) #### 8. **Performance: Unnecessary Clones** Lines 257-258 in `weave.rs`: ```rust let value_to_send = value.clone(); // ... if resume_chan.sender.send(value_to_send).is_err() { ``` You clone the value even if send might fail. Consider: ```rust if resume_chan.sender.send(value).is_err() { // Don't need value anymore ``` Though Arc clones are cheap, avoiding unnecessary work is better. #### 9. **Missing Debug Trait Implementation** WeaveCtx in `value.rs` is not included in any Debug implementations. This makes debugging harder. #### 10. **Serialization Error Message** (crates/runtime/src/serialize.rs:193) ```rust Value::WeaveCtx { .. } => Err(SerializeError::ChannelNotSerializable), ``` The error message says "ChannelNotSerializable" for a WeaveCtx. Consider adding a dedicated `WeaveNotSerializable` error for clarity. --- ## Security Assessment **No security concerns identified.** The implementation: - Doesn't introduce unsafe memory access patterns beyond standard stack operations - Uses proper Arc reference counting - Channel operations are already vetted in the may crate - No new FFI boundaries or external data handling --- ## Performance Considerations **Overall: Good performance characteristics** Pros: - Zero-mutex design eliminates lock contention - O(1) Arc clones for handle passing - Channel-backed communication reuses existing, optimized infrastructure Potential concerns: - Each weave spawns a real may coroutine (not zero-cost) - Channel send/recv involves may's scheduler (context switch overhead) - For tight loops, function calls + TCO might be faster than yield/resume **Recommendation**: Add benchmark comparing weave-based iteration vs recursive approaches for the examples (counter, fibonacci, etc.) --- ## Code Quality & Style **Excellent overall.** The code follows Rust best practices: - Clear module organization - Proper use of unsafe with safety comments - Consistent error handling patterns (mostly) - Good use of Arc vs Box - Tagged stack implementation is clean Minor style notes: - Some functions exceed 100 lines (e.g., `patch_seq_weave`). Consider extracting helpers for Quotation vs Closure paths. - Consistent use of `unsafe { ... }` blocks --- ## Testing Based on the commit messages, you report: - ✅ 113 integration tests pass - ✅ All examples compile - ✅ No lint issues **Recommendations**: 1. Add tests for edge cases mentioned above 2. Add a benchmark suite for weave operations 3. Consider fuzz testing the yield/resume interaction --- ## Documentation **Good but could be expanded:** What's missing: - Update to main ARCHITECTURE.md explaining weaves - No update to README.md mentioning the new feature - Language guide doesn't mention weaves (docs/language-guide.md?) - Migration guide for users? --- ## API Design The explicit context threading is elegant but verbose. Compare: **Current**: ```seq : counter-loop ( Ctx Int -- ) tuck yield rot i.add counter-loop ; ``` **Alternative (if implicit)**: ```seq : counter-loop ( Int -- ) dup yield i.add counter-loop ; ``` The explicit approach is more principled and avoids hidden state. Good choice for a systems language. **Question**: Is the plan to add effect tracking (as mentioned in issue #131 comments) in a future PR? That would make the context requirement compile-time verifiable. --- ## Recommendations Summary ### Must Fix 1. Fix potential memory leak in weave completion (line 134, 188) 2. Document or fix yield panic behavior (line 330) 3. Add stack depth validation (line 240, 306) ### Should Fix 4. Document type system limitation for WeaveCtx 5. Add missing test coverage 6. Clarify auto-closure conversion for weave ### Nice to Have 7. Optimize unnecessary clones 8. Add Debug implementations 9. Improve error messages 10. Update architecture docs 11. Add benchmarks --- ## Conclusion This is **high-quality work** that successfully implements a complex feature with excellent architecture. The zero-mutex design is particularly impressive and shows deep understanding of the existing concurrency model. The issues identified are primarily: - Memory safety edge cases that need verification - Missing test coverage for edge cases - Documentation gaps Once the critical issues are addressed, this is ready to merge. **Estimated effort to address critical issues: 1-2 hours** Great work! 🎉
claude[bot] commented 2025-12-30 06:33:05 +00:00 (Migrated from github.com)

Code Review: Weave (Generator) Implementation

This PR implements generator/coroutine support ("weaves") for Seq, addressing issue #131. The implementation is comprehensive and well-designed. Below is my detailed review.


Strengths

1. Excellent Architecture - Zero-Mutex Design

The weave implementation follows the same lock-free pattern as channels by passing communication handles on the stack rather than using a global registry. This is elegant and avoids contention.

// WeaveCtx travels on the stack - no global state\!
Value::WeaveCtx {
    yield_chan: Arc<ChannelData>,
    resume_chan: Arc<ChannelData>,
}

2. Type System Integration

The addition of computational effects to the type system is well-designed:

  • SideEffect::Yield properly captures the yielded type
  • Effect propagation through statements is correctly implemented
  • Error messages guide users to add effect annotations
  • The | Yield T syntax is clear and follows conventions

3. Consistent API Naming

Renaming spawn → strand.spawn and adding strand.weave, strand.resume creates a coherent namespace. Good consistency.

4. Comprehensive Testing

  • Integration tests cover basic yield/resume flow
  • Tests for immediate completion (no yields)
  • Counter example demonstrates real-world usage
  • All 113 integration tests pass

5. Good Documentation

The inline comments and module documentation clearly explain:

  • The zero-mutex design rationale
  • API contracts and stack effects
  • The flow of values through channels

🔍 Issues & Concerns

CRITICAL: Resource Leak Risk

Location: crates/runtime/src/weave.rs:99-141, crates/runtime/src/weave.rs:160-196

Issue: If a weave is created but never resumed to completion, the spawned coroutine will hang forever waiting on resume_chan.receiver.recv(), and the active strand count will never decrement.

// Line 111-117: Blocks forever if never resumed
let first_value = match weave_ctx_resume.receiver.recv() {
    Ok(v) => v,
    Err(_) => {
        cleanup_strand();
        return;
    }
};

Scenario:

[ counter-body ] strand.weave  # Creates weave, spawns coroutine
# If we never call strand.resume and just drop the handle...
# The coroutine hangs forever waiting for first resume\!

Impact:

  • Memory leak (stack allocation never freed)
  • Strand count never decrements
  • Program may hang on shutdown if using wait_all_strands()

Recommended Fix:
Implement a Drop handler for WeaveHandle or use weak references with timeout:

// Option 1: Add finalizer when handle is dropped
impl Drop for WeaveHandle {
    fn drop(&mut self) {
        // Send poison value to wake waiting weave
        let _ = self.resume_chan.sender.send(POISON_VALUE);
    }
}

// Option 2: Use recv_timeout in weave spawn
let first_value = match weave_ctx_resume.receiver.recv_timeout(timeout) {
    Ok(v) => v,
    Err(_) => {
        cleanup_strand();
        return;
    }
};

HIGH: Panic in Production Code

Location: crates/runtime/src/weave.rs:321, 331-332, 337

Issue: The yield function uses panic\! for error handling. In a language runtime, panics should be reserved for unrecoverable bugs, not user errors.

_ => panic\!(
    "yield: expected WeaveCtx on stack, got {:?}...",
    ctx
),

Better approach: Return error values on the stack (like channels do with success flags):

# Current: panics
yield  # ( Ctx a -- Ctx a )

# Better: return success flag
yield  # ( Ctx a -- Ctx a Bool )

Or use Seq's type system to enforce yield only in weave contexts at compile time (which you're already doing with effects!).

Severity: Medium - The type system already prevents misuse, but runtime defense is good practice.


MEDIUM: Effect Matching Too Lenient

Location: crates/compiler/src/typechecker.rs:340-354

for inferred in &inferred_effects {
    if \!self.effect_matches_any(inferred, &declared_effect.effects) {
        return Err(...);
    }
}

Issue: The check only verifies that inferred effects are declared, but doesn't verify that ALL declared effects are actually used. This allows declaring effects that don't occur:

: no-yield ( -- | Yield Int )  # Declares Yield but never yields\!
  42 ;

Impact:

  • False effect annotations mislead users
  • Breaks effect system soundness
  • Could hide bugs where yields are accidentally removed

Recommended Fix:

// Also check that declared effects are actually produced
for declared in &declared_effect.effects {
    if \!self.effect_matches_any(declared, &inferred_effects) {
        return Err(format\!(
            "Word '{}': declares effect '{}' but body doesn't produce it",
            word.name, declared
        ));
    }
}

MEDIUM: Missing effect_matches_any Implementation

Location: crates/compiler/src/typechecker.rs:340

The code calls self.effect_matches_any() but this method is not defined in the diff. Is it implemented elsewhere? If not, the code won't compile.

Action Needed: Verify this method exists or add it:

fn effect_matches_any(&self, effect: &SideEffect, effects: &[SideEffect]) -> bool {
    effects.iter().any(|e| match (effect, e) {
        (SideEffect::Yield(t1), SideEffect::Yield(t2)) => {
            // Type variables match by kind, concrete types must unify
            matches\!((t1.as_ref(), t2.as_ref()), 
                (Type::Var(_), Type::Var(_)) | 
                (t1, t2) if t1 == t2)
        }
    })
}

LOW: Inconsistent Naming

Location: crates/runtime/src/weave.rs:348

pub use patch_seq_yield as weave_yield;

Issue: The function is named weave_yield in the export but yield in Seq. This inconsistency could confuse developers.

Suggestion: Use yield or patch_seq_weave_yield for consistency:

pub use patch_seq_yield as yield_value;  // More descriptive

LOW: Sentinel Value Magic Number

Location: crates/runtime/src/weave.rs:40

const DONE_SENTINEL: i64 = i64::MIN;

Concern: Using i64::MIN as a sentinel is fragile. If a user actually yields i64::MIN, it would be misinterpreted as completion.

Probability: Low, but edge case exists.

Better approach: Use a dedicated enum or variant type:

enum WeaveMessage {
    Value(Value),
    Done,
}

Or reserve a special Value variant:

Value::WeaveDone  // No payload needed

LOW: Clone Overhead

Location: crates/runtime/src/weave.rs:258, 327

let value_to_send = value.clone();

Issue: Values are cloned before sending through channels. For large variants or strings, this could be expensive.

Consideration:

  • Since channels take ownership anyway, could we move instead of clone?
  • If clone is necessary (value used after send), document why

Impact: Minor performance concern, likely acceptable for generator use cases.


INFO: Test Coverage Gaps

Location: tests/integration/src/test-weave.seq

Missing test cases:

  1. Nested weaves - Can a weave create another weave?
  2. Weave with closure - Does capture analysis work correctly?
  3. Error handling - What happens if resume channel closes unexpectedly?
  4. Multiple yields - Stress test with 1000+ yield/resume cycles
  5. Concurrent weaves - Multiple weaves running on different strands
  6. Type polymorphism - Weave yielding different types with type variables

Recommendation: Add these test cases to ensure robustness.


🛡️ Security Considerations

1. Stack Overflow

The counter-loop example uses tail recursion to create an infinite generator. Verify TCO works correctly for weaves - if TCO fails, stack overflow is possible.

Test needed:

: stress-test-tco ( Ctx Int -- | Yield Int )
  # Yield 100,000 times to verify TCO
  dup 100000 i.< if
    dup yield drop
    1 i.add stress-test-tco  # Must be tail call
  else
    drop
  then
;

2. Use-After-Free Prevention

Verify that dropping a WeaveHandle doesn't cause use-after-free if the weave is still running. The Arc<ChannelData> should prevent this, but worth explicit testing.


📊 Performance Considerations

1. Channel Allocation

Each weave creates two MPMC channels. For high-frequency generator creation, this could add overhead.

Benchmark needed:

  • Time to create 10,000 weaves
  • Compare to equivalent channel-based solution

2. Context Switch Cost

Every yield involves two channel operations (send + recv). May's coroutine switch is ~100ns, but channel ops might add latency.

Benchmark needed:

  • Time 1,000,000 yield/resume cycles
  • Compare to Python generators or Lua coroutines

3. Memory Footprint

Each weave has:

  • Coroutine stack (default 128KB)
  • Two channels (minimal metadata)
  • WeaveCtx value on stack

For applications creating thousands of weaves, this could add up.


📝 Documentation Suggestions

1. Add Migration Guide

For users upgrading, document the spawn → strand.spawn rename:

## Breaking Changes in v0.X

- `spawn` renamed to `strand.spawn` for consistency
- Update existing code: `s/spawn/strand.spawn/g`

2. Document Effect System

Add examples to TYPE_SYSTEM_GUIDE.md:

# Effect declarations
: yielder ( Ctx Int -- Ctx Int | Yield Int )
  tuck yield ;

# Effects propagate
: calls-yielder ( Ctx Int -- Ctx Int | Yield Int )
  yielder ;  # Inherits Yield effect

3. Add "Weave vs Strand" Guide

Help users choose between strand.spawn and strand.weave:

## When to use strand.spawn:
- Fire-and-forget concurrency
- Independent parallel work
- Communicate via channels

## When to use strand.weave:
- Lazy sequences
- Iterators
- Backtracking (Prolog)
- Resume-based state machines

Best Practices Followed

  1. Backwards compatibility - Existing benchmarks updated, no breakage
  2. Effect system - Proper tracking of computational effects
  3. Zero-mutex design - Excellent concurrency story
  4. CI passes - All tests green
  5. Consistent naming - strand.* namespace is clean
  6. Good examples - Counter example is clear and instructive

🎯 Recommendations

Before Merge:

  1. MUST FIX: Address resource leak when weave handle is dropped without resuming to completion
  2. SHOULD FIX: Verify effect_matches_any implementation exists
  3. SHOULD ADD: Test for nested weaves and weave+closure interaction
  4. SHOULD ADD: Documentation about weave lifecycle and cleanup

Post-Merge (Future Work):

  1. Add benchmarks comparing to channel-based generators
  2. Add stress tests (1M+ yield/resume cycles)
  3. Consider adding strand.weave_timeout for bounded execution
  4. Document effect system in TYPE_SYSTEM_GUIDE.md

📊 Summary Score

Category Rating Notes
Code Quality 8/10 Clean, well-structured, good separation of concerns
Architecture 9/10 Excellent zero-mutex design, fits well with existing runtime
Type Safety 8/10 Effect system is solid, minor soundness gap in effect checking
Testing 7/10 Good coverage, but missing edge cases
Documentation 7/10 Good inline docs, needs user-facing guide
Performance 8/10 Good design, needs benchmarks to verify
Security 7/10 Resource leak risk needs addressing

Overall: 8/10 - Excellent implementation with minor issues to address.


🏁 Verdict

APPROVE WITH CHANGES REQUESTED

This is a solid implementation of a complex feature. The architecture is excellent and the type system integration is well-designed. However, the resource leak issue should be addressed before merge.

The PR demonstrates strong understanding of Seq's architecture and makes thoughtful design choices (zero-mutex, effect system, TCO compatibility). With the critical issue fixed, this will be a valuable addition to the language.

Great work! 🎉


Review conducted with attention to: code quality, type safety, concurrency correctness, memory safety, performance, and documentation quality, following the repository's architectural principles.

## Code Review: Weave (Generator) Implementation This PR implements generator/coroutine support ("weaves") for Seq, addressing issue #131. The implementation is comprehensive and well-designed. Below is my detailed review. --- ## ✅ Strengths ### 1. **Excellent Architecture - Zero-Mutex Design** The weave implementation follows the same lock-free pattern as channels by passing communication handles on the stack rather than using a global registry. This is elegant and avoids contention. ```rust // WeaveCtx travels on the stack - no global state\! Value::WeaveCtx { yield_chan: Arc<ChannelData>, resume_chan: Arc<ChannelData>, } ``` ### 2. **Type System Integration** The addition of computational effects to the type system is well-designed: - `SideEffect::Yield` properly captures the yielded type - Effect propagation through statements is correctly implemented - Error messages guide users to add effect annotations - The `| Yield T` syntax is clear and follows conventions ### 3. **Consistent API Naming** Renaming `spawn → strand.spawn` and adding `strand.weave`, `strand.resume` creates a coherent namespace. Good consistency. ### 4. **Comprehensive Testing** - Integration tests cover basic yield/resume flow - Tests for immediate completion (no yields) - Counter example demonstrates real-world usage - All 113 integration tests pass ### 5. **Good Documentation** The inline comments and module documentation clearly explain: - The zero-mutex design rationale - API contracts and stack effects - The flow of values through channels --- ## 🔍 Issues & Concerns ### **CRITICAL: Resource Leak Risk** **Location:** `crates/runtime/src/weave.rs:99-141`, `crates/runtime/src/weave.rs:160-196` **Issue:** If a weave is created but never resumed to completion, the spawned coroutine will hang forever waiting on `resume_chan.receiver.recv()`, and the active strand count will never decrement. ```rust // Line 111-117: Blocks forever if never resumed let first_value = match weave_ctx_resume.receiver.recv() { Ok(v) => v, Err(_) => { cleanup_strand(); return; } }; ``` **Scenario:** ```seq [ counter-body ] strand.weave # Creates weave, spawns coroutine # If we never call strand.resume and just drop the handle... # The coroutine hangs forever waiting for first resume\! ``` **Impact:** - Memory leak (stack allocation never freed) - Strand count never decrements - Program may hang on shutdown if using `wait_all_strands()` **Recommended Fix:** Implement a `Drop` handler for WeaveHandle or use weak references with timeout: ```rust // Option 1: Add finalizer when handle is dropped impl Drop for WeaveHandle { fn drop(&mut self) { // Send poison value to wake waiting weave let _ = self.resume_chan.sender.send(POISON_VALUE); } } // Option 2: Use recv_timeout in weave spawn let first_value = match weave_ctx_resume.receiver.recv_timeout(timeout) { Ok(v) => v, Err(_) => { cleanup_strand(); return; } }; ``` --- ### **HIGH: Panic in Production Code** **Location:** `crates/runtime/src/weave.rs:321`, `331-332`, `337` **Issue:** The `yield` function uses `panic\!` for error handling. In a language runtime, panics should be reserved for unrecoverable bugs, not user errors. ```rust _ => panic\!( "yield: expected WeaveCtx on stack, got {:?}...", ctx ), ``` **Better approach:** Return error values on the stack (like channels do with success flags): ```seq # Current: panics yield # ( Ctx a -- Ctx a ) # Better: return success flag yield # ( Ctx a -- Ctx a Bool ) ``` Or use Seq's type system to enforce `yield` only in weave contexts at compile time (which you're already doing with effects\!). **Severity:** Medium - The type system already prevents misuse, but runtime defense is good practice. --- ### **MEDIUM: Effect Matching Too Lenient** **Location:** `crates/compiler/src/typechecker.rs:340-354` ```rust for inferred in &inferred_effects { if \!self.effect_matches_any(inferred, &declared_effect.effects) { return Err(...); } } ``` **Issue:** The check only verifies that inferred effects are declared, but doesn't verify that ALL declared effects are actually used. This allows declaring effects that don't occur: ```seq : no-yield ( -- | Yield Int ) # Declares Yield but never yields\! 42 ; ``` **Impact:** - False effect annotations mislead users - Breaks effect system soundness - Could hide bugs where yields are accidentally removed **Recommended Fix:** ```rust // Also check that declared effects are actually produced for declared in &declared_effect.effects { if \!self.effect_matches_any(declared, &inferred_effects) { return Err(format\!( "Word '{}': declares effect '{}' but body doesn't produce it", word.name, declared )); } } ``` --- ### **MEDIUM: Missing `effect_matches_any` Implementation** **Location:** `crates/compiler/src/typechecker.rs:340` The code calls `self.effect_matches_any()` but this method is not defined in the diff. Is it implemented elsewhere? If not, the code won't compile. **Action Needed:** Verify this method exists or add it: ```rust fn effect_matches_any(&self, effect: &SideEffect, effects: &[SideEffect]) -> bool { effects.iter().any(|e| match (effect, e) { (SideEffect::Yield(t1), SideEffect::Yield(t2)) => { // Type variables match by kind, concrete types must unify matches\!((t1.as_ref(), t2.as_ref()), (Type::Var(_), Type::Var(_)) | (t1, t2) if t1 == t2) } }) } ``` --- ### **LOW: Inconsistent Naming** **Location:** `crates/runtime/src/weave.rs:348` ```rust pub use patch_seq_yield as weave_yield; ``` **Issue:** The function is named `weave_yield` in the export but `yield` in Seq. This inconsistency could confuse developers. **Suggestion:** Use `yield` or `patch_seq_weave_yield` for consistency: ```rust pub use patch_seq_yield as yield_value; // More descriptive ``` --- ### **LOW: Sentinel Value Magic Number** **Location:** `crates/runtime/src/weave.rs:40` ```rust const DONE_SENTINEL: i64 = i64::MIN; ``` **Concern:** Using `i64::MIN` as a sentinel is fragile. If a user actually yields `i64::MIN`, it would be misinterpreted as completion. **Probability:** Low, but edge case exists. **Better approach:** Use a dedicated enum or variant type: ```rust enum WeaveMessage { Value(Value), Done, } ``` Or reserve a special `Value` variant: ```rust Value::WeaveDone // No payload needed ``` --- ### **LOW: Clone Overhead** **Location:** `crates/runtime/src/weave.rs:258`, `327` ```rust let value_to_send = value.clone(); ``` **Issue:** Values are cloned before sending through channels. For large variants or strings, this could be expensive. **Consideration:** - Since channels take ownership anyway, could we move instead of clone? - If clone is necessary (value used after send), document why **Impact:** Minor performance concern, likely acceptable for generator use cases. --- ### **INFO: Test Coverage Gaps** **Location:** `tests/integration/src/test-weave.seq` **Missing test cases:** 1. **Nested weaves** - Can a weave create another weave? 2. **Weave with closure** - Does capture analysis work correctly? 3. **Error handling** - What happens if resume channel closes unexpectedly? 4. **Multiple yields** - Stress test with 1000+ yield/resume cycles 5. **Concurrent weaves** - Multiple weaves running on different strands 6. **Type polymorphism** - Weave yielding different types with type variables **Recommendation:** Add these test cases to ensure robustness. --- ## 🛡️ Security Considerations ### 1. **Stack Overflow** The `counter-loop` example uses tail recursion to create an infinite generator. Verify TCO works correctly for weaves - if TCO fails, stack overflow is possible. **Test needed:** ```seq : stress-test-tco ( Ctx Int -- | Yield Int ) # Yield 100,000 times to verify TCO dup 100000 i.< if dup yield drop 1 i.add stress-test-tco # Must be tail call else drop then ; ``` ### 2. **Use-After-Free Prevention** Verify that dropping a WeaveHandle doesn't cause use-after-free if the weave is still running. The `Arc<ChannelData>` should prevent this, but worth explicit testing. --- ## 📊 Performance Considerations ### 1. **Channel Allocation** Each weave creates two MPMC channels. For high-frequency generator creation, this could add overhead. **Benchmark needed:** - Time to create 10,000 weaves - Compare to equivalent channel-based solution ### 2. **Context Switch Cost** Every `yield` involves two channel operations (send + recv). May's coroutine switch is ~100ns, but channel ops might add latency. **Benchmark needed:** - Time 1,000,000 yield/resume cycles - Compare to Python generators or Lua coroutines ### 3. **Memory Footprint** Each weave has: - Coroutine stack (default 128KB) - Two channels (minimal metadata) - WeaveCtx value on stack For applications creating thousands of weaves, this could add up. --- ## 📝 Documentation Suggestions ### 1. **Add Migration Guide** For users upgrading, document the `spawn → strand.spawn` rename: ```markdown ## Breaking Changes in v0.X - `spawn` renamed to `strand.spawn` for consistency - Update existing code: `s/spawn/strand.spawn/g` ``` ### 2. **Document Effect System** Add examples to `TYPE_SYSTEM_GUIDE.md`: ```seq # Effect declarations : yielder ( Ctx Int -- Ctx Int | Yield Int ) tuck yield ; # Effects propagate : calls-yielder ( Ctx Int -- Ctx Int | Yield Int ) yielder ; # Inherits Yield effect ``` ### 3. **Add "Weave vs Strand" Guide** Help users choose between `strand.spawn` and `strand.weave`: ```markdown ## When to use strand.spawn: - Fire-and-forget concurrency - Independent parallel work - Communicate via channels ## When to use strand.weave: - Lazy sequences - Iterators - Backtracking (Prolog) - Resume-based state machines ``` --- ## ✅ Best Practices Followed 1. ✅ **Backwards compatibility** - Existing benchmarks updated, no breakage 2. ✅ **Effect system** - Proper tracking of computational effects 3. ✅ **Zero-mutex design** - Excellent concurrency story 4. ✅ **CI passes** - All tests green 5. ✅ **Consistent naming** - `strand.*` namespace is clean 6. ✅ **Good examples** - Counter example is clear and instructive --- ## 🎯 Recommendations ### Before Merge: 1. **MUST FIX:** Address resource leak when weave handle is dropped without resuming to completion 2. **SHOULD FIX:** Verify `effect_matches_any` implementation exists 3. **SHOULD ADD:** Test for nested weaves and weave+closure interaction 4. **SHOULD ADD:** Documentation about weave lifecycle and cleanup ### Post-Merge (Future Work): 1. Add benchmarks comparing to channel-based generators 2. Add stress tests (1M+ yield/resume cycles) 3. Consider adding `strand.weave_timeout` for bounded execution 4. Document effect system in `TYPE_SYSTEM_GUIDE.md` --- ## 📊 Summary Score | Category | Rating | Notes | |----------|--------|-------| | **Code Quality** | 8/10 | Clean, well-structured, good separation of concerns | | **Architecture** | 9/10 | Excellent zero-mutex design, fits well with existing runtime | | **Type Safety** | 8/10 | Effect system is solid, minor soundness gap in effect checking | | **Testing** | 7/10 | Good coverage, but missing edge cases | | **Documentation** | 7/10 | Good inline docs, needs user-facing guide | | **Performance** | 8/10 | Good design, needs benchmarks to verify | | **Security** | 7/10 | Resource leak risk needs addressing | **Overall: 8/10** - Excellent implementation with minor issues to address. --- ## 🏁 Verdict **APPROVE WITH CHANGES REQUESTED** This is a solid implementation of a complex feature. The architecture is excellent and the type system integration is well-designed. However, the resource leak issue should be addressed before merge. The PR demonstrates strong understanding of Seq's architecture and makes thoughtful design choices (zero-mutex, effect system, TCO compatibility). With the critical issue fixed, this will be a valuable addition to the language. Great work\! 🎉 --- *Review conducted with attention to: code quality, type safety, concurrency correctness, memory safety, performance, and documentation quality, following the repository's architectural principles.*
claude[bot] commented 2025-12-30 06:49:03 +00:00 (Migrated from github.com)

Pull Request Review: Issue #131 - Generator/Weave Support

Summary

This PR successfully implements generator/coroutine support ("weaves") for Seq, enabling lazy sequences, streaming, and backtracking patterns. The implementation is well-designed, leveraging the existing May coroutine infrastructure with bidirectional channel-based communication.

Overall Assessment: Excellent implementation with minor recommendations


🎯 Strengths

1. Zero-Mutex Design

The weave architecture follows the same philosophy as channels - no global registry, no mutex contention. WeaveCtx travels on the stack as a value, which is elegant and avoids synchronization overhead.

// crates/runtime/src/weave.rs:232
let handle = Value::WeaveCtx {
    yield_chan: handle_yield,
    resume_chan: handle_resume,
};

2. Type System Integration

The computational effects system is well-designed:

  • SideEffect::Yield properly tracks generator effects
  • Bidirectional type checking ensures declared effects match actual usage
  • Effect propagation through quotations is correctly handled
// crates/compiler/src/types.rs:12-22
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SideEffect {
    Yield(Box<Type>),
}

The error messages are helpful:

Word 'foo': body produces effect 'Yield Int' but no matching effect is declared.
Hint: Add '| Yield <type>' to the word's stack effect declaration.

3. Documentation Quality

Excellent inline documentation, particularly in:

  • crates/runtime/src/weave.rs (architecture, resource management, limitations)
  • Test files clearly explain usage patterns
  • The example in examples/weave/counter.seq is comprehensive

4. Consistent Naming

The rename from spawn to strand.spawn improves clarity and namespacing. The new names (strand.weave, strand.resume) are intuitive.


🐛 Potential Issues

1. Resource Leak Risk (High Priority)

Location: crates/runtime/src/weave.rs:32-49

The documentation warns about resource leaks if weaves aren't resumed to completion:

/// **Important:** Weaves MUST be resumed until completion for proper cleanup.
/// If a WeaveHandle is dropped without running the weave to completion, the
/// spawned coroutine will hang forever waiting on resume_chan.

Problem: This is a footgun for users. Unlike channels (which can be safely dropped), weaves require explicit cleanup.

Recommendations:

  1. Add a destructor check - Implement Drop for WeaveCtx that panics or logs if dropped while still active:
impl Drop for WeaveCtx {
    fn drop(&mut self) {
        // Check if weave is still active
        if self.is_active() {
            eprintln!("Warning: WeaveCtx dropped while still active. This may leak resources.");
            // Or: panic!("Must resume weave to completion before dropping");
        }
    }
}
  1. Track weave state - Add a state field to detect incomplete weaves.

  2. Document escape hatches - Provide a weave.cancel() or similar for explicit early termination.

2. i64::MIN Sentinel Conflict (Medium Priority)

Location: crates/runtime/src/weave.rs:62-67

/// **Warning:** This means `i64::MIN` cannot be yielded from a weave.
/// If a weave yields this exact value, it will be misinterpreted as completion.
const DONE_SENTINEL: i64 = i64::MIN;

Problem: Silent data corruption if user yields -9223372036854775808.

Recommendations:

  1. Use a dedicated completion message type:
enum WeaveMessage {
    Yield(Value),
    Done,
}

Send WeaveMessage::Yield(value) for normal yields and WeaveMessage::Done for completion. This eliminates the sentinel entirely.

  1. Alternative: Check for i64::MIN in patch_seq_yield and panic with a clear error.

3. Missing Serialization Support (Low Priority)

Location: crates/runtime/src/serialize.rs:193

Value::WeaveCtx { .. } => Err(SerializeError::ChannelNotSerializable),

Note: Currently reuses ChannelNotSerializable error. Consider adding:

WeaveNotSerializable

This is fine for now since weaves inherently can't be serialized (they contain runtime state), but a specific error type would be clearer.

4. Type Safety: Effect Polymorphism

Location: crates/compiler/src/typechecker.rs:920-923

// Quotations don't propagate effects - they capture them in the quotation type
Ok((result_stack, Subst::empty(), vec![]))

Question: Can quotations with Yield effects be passed to strand.spawn?

Looking at line 941-942:

let adjusted_stack = if name == "strand.spawn" {
    self.adjust_stack_for_spawn(current_stack, &fresh_effect)?

Recommendation: Verify that strand.spawn rejects quotations with Yield effects (since spawn is fire-and-forget). Consider adding a test:

# Should fail: spawn requires pure quotations
: yielding-body ( Ctx Int -- | Yield Int )
  yield
;

[ yielding-body ] strand.spawn  # Should be a type error

Performance Considerations

1. Channel Overhead

Each weave creates two MPMC channels. For fine-grained generators (e.g., byte-level streaming), this might be overkill.

Mitigation: Document recommended use cases (coarse-grained generators). For high-frequency yields, users should batch values.

2. Arc Cloning

Channels are wrapped in Arc and cloned frequently:

// crates/runtime/src/weave.rs:99-101
let weave_ctx_yield = Arc::clone(&yield_chan);
let weave_ctx_resume = Arc::clone(&resume_chan);

Status: This is correct and necessary. Arc cloning is cheap (atomic increment), and the design avoids locks, so performance is good.

3. Stack Cloning for Quotations

Location: crates/runtime/src/weave.rs:120

let (child_stack, child_base) = unsafe { crate::stack::clone_stack_with_base(stack) };

Observation: Stack cloning for quotations (vs. fresh stack for closures) is correct but potentially expensive for deep stacks.

Recommendation: Mention in documentation that weaves with quotations inherit the creation stack (like strand.spawn).


🔒 Security Concerns

1. Unsafe Code Review

The unsafe code looks correct:

#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_weave(stack: Stack) -> Stack {
    // ...
    let (stack, quot_value) = unsafe { pop(stack) };
    // ...
}

Verification:

  • Stack non-null checks: assert!(!stack.is_null())
  • Type matching before transmute: Pattern match on Value::Quotation before std::mem::transmute(wrapper)
  • Pointer validity: child_base.is_null() check before use
  • Send safety: Channels wrapped in Arc (Send + Sync)

No issues found.

2. Channel Cleanup on Panic

If the weave quotation panics, the channels are dropped (via RAII), so no leak occurs. The caller will get a channel error on the next resume. Good!


🧪 Test Coverage

Positive Aspects:

  1. New test file: tests/integration/src/test-weave.seq
  2. Example program: examples/weave/counter.seq
  3. Tests cover:
    • Basic yield/resume flow
    • Completion detection
    • Immediate completion (no yields)

Gaps:

  1. No test for resource leaks - Should have a test that intentionally leaks a weave and documents expected behavior.

  2. No test for Yield effect type checking - Add negative tests:

    # Should fail: missing Yield effect
    : bad-weave ( Ctx Int -- Ctx )
      1 yield  # ERROR: body yields but effect not declared
    ;
    
  3. No test for strand.spawn + Yield rejection - Verify that spawning a yielding quotation is rejected.

  4. No stress test - Consider a test that creates/resumes thousands of weaves to verify no memory leaks.

  5. No test for i64::MIN edge case - Document that yielding i64::MIN is unsupported (or add a panic test).

Recommendations:

# Test: weave resource leak detection
: test-weave-leak-warning ( -- )
  [ three-values ] strand.weave
  # Intentionally drop without resuming - should warn
  drop
;

# Test: cannot spawn yielding quotation
: test-spawn-yield-rejected ( -- )
  # This should be a compile-time type error
  [ 1 yield ] strand.spawn  # ERROR
;

📝 Code Quality & Best Practices

1. Consistent Style

  • Follows existing codebase patterns
  • Naming is clear and consistent
  • Comments are helpful without being verbose

2. Error Handling

  • Channels use Result types correctly
  • Panics have clear messages with context
  • Type errors provide actionable hints

3. Backward Compatibility

  • Rename spawnstrand.spawn is breaking but intentional
  • Benchmarks updated correctly
  • Examples updated

4. Documentation ⚠️

Missing:

  1. Update main README.md to mention weaves (currently only mentions spawn)
  2. Add weave example to "What Works" section
  3. Document the i64::MIN limitation prominently

Suggestion for README.md:

**Concurrency:**
- Strands: `strand.spawn` (green threads)
- Weaves: `strand.weave`, `strand.resume`, `yield` (generators/coroutines)
- Channels: `make-channel`, `send`, `receive`, `close-channel`
- TCP: `tcp-listen`, `tcp-accept`, `tcp-read`, `tcp-write`, `tcp-close`

🎯 Recommendations Summary

High Priority:

  1. Add Drop guard for WeaveCtx to detect leaks
  2. Replace i64::MIN sentinel with explicit WeaveMessage enum
  3. Add negative test for Yield effect type checking
  4. Update README.md with weave documentation

Medium Priority:

  1. Add test for strand.spawn + Yield rejection
  2. Document performance characteristics (channel overhead, batching recommendations)
  3. Add stress test for leak detection

Low Priority:

  1. ⚠️ Add WeaveNotSerializable error variant (or document reuse of ChannelNotSerializable)
  2. ⚠️ Consider escape hatch for early weave cancellation

Approval Decision

Status: Approved with recommendations

This is a well-designed feature that integrates cleanly with Seq's existing architecture. The type system integration is particularly impressive. Address the high-priority items (resource leak detection, sentinel replacement) before merging, but the core implementation is solid.

The PR successfully achieves its goal of enabling seq-prolog and other generator-based patterns. Great work! 🚀


Reviewed by: Claude Code
Review Date: 2025-12-30

# Pull Request Review: Issue #131 - Generator/Weave Support ## Summary This PR successfully implements generator/coroutine support ("weaves") for Seq, enabling lazy sequences, streaming, and backtracking patterns. The implementation is well-designed, leveraging the existing May coroutine infrastructure with bidirectional channel-based communication. **Overall Assessment: ✅ Excellent implementation with minor recommendations** --- ## 🎯 Strengths ### 1. **Zero-Mutex Design** The weave architecture follows the same philosophy as channels - no global registry, no mutex contention. WeaveCtx travels on the stack as a value, which is elegant and avoids synchronization overhead. ```rust // crates/runtime/src/weave.rs:232 let handle = Value::WeaveCtx { yield_chan: handle_yield, resume_chan: handle_resume, }; ``` ### 2. **Type System Integration** The computational effects system is well-designed: - `SideEffect::Yield` properly tracks generator effects - Bidirectional type checking ensures declared effects match actual usage - Effect propagation through quotations is correctly handled ```rust // crates/compiler/src/types.rs:12-22 #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum SideEffect { Yield(Box<Type>), } ``` The error messages are helpful: ``` Word 'foo': body produces effect 'Yield Int' but no matching effect is declared. Hint: Add '| Yield <type>' to the word's stack effect declaration. ``` ### 3. **Documentation Quality** Excellent inline documentation, particularly in: - `crates/runtime/src/weave.rs` (architecture, resource management, limitations) - Test files clearly explain usage patterns - The example in `examples/weave/counter.seq` is comprehensive ### 4. **Consistent Naming** The rename from `spawn` to `strand.spawn` improves clarity and namespacing. The new names (`strand.weave`, `strand.resume`) are intuitive. --- ## 🐛 Potential Issues ### 1. **Resource Leak Risk (High Priority)** **Location:** `crates/runtime/src/weave.rs:32-49` The documentation warns about resource leaks if weaves aren't resumed to completion: ```rust /// **Important:** Weaves MUST be resumed until completion for proper cleanup. /// If a WeaveHandle is dropped without running the weave to completion, the /// spawned coroutine will hang forever waiting on resume_chan. ``` **Problem:** This is a footgun for users. Unlike channels (which can be safely dropped), weaves require explicit cleanup. **Recommendations:** 1. **Add a destructor check** - Implement `Drop` for `WeaveCtx` that panics or logs if dropped while still active: ```rust impl Drop for WeaveCtx { fn drop(&mut self) { // Check if weave is still active if self.is_active() { eprintln!("Warning: WeaveCtx dropped while still active. This may leak resources."); // Or: panic!("Must resume weave to completion before dropping"); } } } ``` 2. **Track weave state** - Add a state field to detect incomplete weaves. 3. **Document escape hatches** - Provide a `weave.cancel()` or similar for explicit early termination. ### 2. **i64::MIN Sentinel Conflict (Medium Priority)** **Location:** `crates/runtime/src/weave.rs:62-67` ```rust /// **Warning:** This means `i64::MIN` cannot be yielded from a weave. /// If a weave yields this exact value, it will be misinterpreted as completion. const DONE_SENTINEL: i64 = i64::MIN; ``` **Problem:** Silent data corruption if user yields `-9223372036854775808`. **Recommendations:** 1. **Use a dedicated completion message type:** ```rust enum WeaveMessage { Yield(Value), Done, } ``` Send `WeaveMessage::Yield(value)` for normal yields and `WeaveMessage::Done` for completion. This eliminates the sentinel entirely. 2. **Alternative:** Check for `i64::MIN` in `patch_seq_yield` and panic with a clear error. ### 3. **Missing Serialization Support (Low Priority)** **Location:** `crates/runtime/src/serialize.rs:193` ```rust Value::WeaveCtx { .. } => Err(SerializeError::ChannelNotSerializable), ``` **Note:** Currently reuses `ChannelNotSerializable` error. Consider adding: ```rust WeaveNotSerializable ``` This is fine for now since weaves inherently can't be serialized (they contain runtime state), but a specific error type would be clearer. ### 4. **Type Safety: Effect Polymorphism** **Location:** `crates/compiler/src/typechecker.rs:920-923` ```rust // Quotations don't propagate effects - they capture them in the quotation type Ok((result_stack, Subst::empty(), vec![])) ``` **Question:** Can quotations with `Yield` effects be passed to `strand.spawn`? Looking at line 941-942: ```rust let adjusted_stack = if name == "strand.spawn" { self.adjust_stack_for_spawn(current_stack, &fresh_effect)? ``` **Recommendation:** Verify that `strand.spawn` rejects quotations with `Yield` effects (since spawn is fire-and-forget). Consider adding a test: ```seq # Should fail: spawn requires pure quotations : yielding-body ( Ctx Int -- | Yield Int ) yield ; [ yielding-body ] strand.spawn # Should be a type error ``` --- ## ⚡ Performance Considerations ### 1. **Channel Overhead** Each weave creates two MPMC channels. For fine-grained generators (e.g., byte-level streaming), this might be overkill. **Mitigation:** Document recommended use cases (coarse-grained generators). For high-frequency yields, users should batch values. ### 2. **Arc Cloning** Channels are wrapped in `Arc` and cloned frequently: ```rust // crates/runtime/src/weave.rs:99-101 let weave_ctx_yield = Arc::clone(&yield_chan); let weave_ctx_resume = Arc::clone(&resume_chan); ``` **Status:** This is correct and necessary. Arc cloning is cheap (atomic increment), and the design avoids locks, so performance is good. ### 3. **Stack Cloning for Quotations** **Location:** `crates/runtime/src/weave.rs:120` ```rust let (child_stack, child_base) = unsafe { crate::stack::clone_stack_with_base(stack) }; ``` **Observation:** Stack cloning for quotations (vs. fresh stack for closures) is correct but potentially expensive for deep stacks. **Recommendation:** Mention in documentation that weaves with quotations inherit the creation stack (like `strand.spawn`). --- ## 🔒 Security Concerns ### 1. **Unsafe Code Review** The unsafe code looks correct: ```rust #[unsafe(no_mangle)] pub unsafe extern "C" fn patch_seq_weave(stack: Stack) -> Stack { // ... let (stack, quot_value) = unsafe { pop(stack) }; // ... } ``` **Verification:** - ✅ Stack non-null checks: `assert!(!stack.is_null())` - ✅ Type matching before transmute: Pattern match on `Value::Quotation` before `std::mem::transmute(wrapper)` - ✅ Pointer validity: `child_base.is_null()` check before use - ✅ Send safety: Channels wrapped in Arc (Send + Sync) **No issues found.** ### 2. **Channel Cleanup on Panic** If the weave quotation panics, the channels are dropped (via RAII), so no leak occurs. The caller will get a channel error on the next resume. Good! --- ## 🧪 Test Coverage ### Positive Aspects: 1. ✅ New test file: `tests/integration/src/test-weave.seq` 2. ✅ Example program: `examples/weave/counter.seq` 3. ✅ Tests cover: - Basic yield/resume flow - Completion detection - Immediate completion (no yields) ### Gaps: 1. **No test for resource leaks** - Should have a test that intentionally leaks a weave and documents expected behavior. 2. **No test for Yield effect type checking** - Add negative tests: ```seq # Should fail: missing Yield effect : bad-weave ( Ctx Int -- Ctx ) 1 yield # ERROR: body yields but effect not declared ; ``` 3. **No test for strand.spawn + Yield rejection** - Verify that spawning a yielding quotation is rejected. 4. **No stress test** - Consider a test that creates/resumes thousands of weaves to verify no memory leaks. 5. **No test for i64::MIN edge case** - Document that yielding `i64::MIN` is unsupported (or add a panic test). ### Recommendations: ```seq # Test: weave resource leak detection : test-weave-leak-warning ( -- ) [ three-values ] strand.weave # Intentionally drop without resuming - should warn drop ; # Test: cannot spawn yielding quotation : test-spawn-yield-rejected ( -- ) # This should be a compile-time type error [ 1 yield ] strand.spawn # ERROR ; ``` --- ## 📝 Code Quality & Best Practices ### 1. **Consistent Style** ✅ - Follows existing codebase patterns - Naming is clear and consistent - Comments are helpful without being verbose ### 2. **Error Handling** ✅ - Channels use Result types correctly - Panics have clear messages with context - Type errors provide actionable hints ### 3. **Backward Compatibility** ✅ - Rename `spawn` → `strand.spawn` is breaking but intentional - Benchmarks updated correctly - Examples updated ### 4. **Documentation** ⚠️ **Missing:** 1. Update main README.md to mention weaves (currently only mentions spawn) 2. Add weave example to "What Works" section 3. Document the i64::MIN limitation prominently **Suggestion for README.md:** ```markdown **Concurrency:** - Strands: `strand.spawn` (green threads) - Weaves: `strand.weave`, `strand.resume`, `yield` (generators/coroutines) - Channels: `make-channel`, `send`, `receive`, `close-channel` - TCP: `tcp-listen`, `tcp-accept`, `tcp-read`, `tcp-write`, `tcp-close` ``` --- ## 🎯 Recommendations Summary ### High Priority: 1. ✅ **Add Drop guard** for WeaveCtx to detect leaks 2. ✅ **Replace i64::MIN sentinel** with explicit WeaveMessage enum 3. ✅ **Add negative test** for Yield effect type checking 4. ✅ **Update README.md** with weave documentation ### Medium Priority: 5. ✅ **Add test** for strand.spawn + Yield rejection 6. ✅ **Document performance characteristics** (channel overhead, batching recommendations) 7. ✅ **Add stress test** for leak detection ### Low Priority: 8. ⚠️ **Add WeaveNotSerializable** error variant (or document reuse of ChannelNotSerializable) 9. ⚠️ **Consider escape hatch** for early weave cancellation --- ## ✅ Approval Decision **Status: Approved with recommendations** This is a well-designed feature that integrates cleanly with Seq's existing architecture. The type system integration is particularly impressive. Address the high-priority items (resource leak detection, sentinel replacement) before merging, but the core implementation is solid. The PR successfully achieves its goal of enabling seq-prolog and other generator-based patterns. Great work! 🚀 --- **Reviewed by:** Claude Code **Review Date:** 2025-12-30
claude[bot] commented 2025-12-30 15:03:19 +00:00 (Migrated from github.com)

PR Review: Generator/Coroutine Support (Issue #131)

This is an impressive implementation of generator support with strong type system integration. The PR delivers a well-architected solution with excellent documentation and test coverage.


🎯 Summary

This PR implements generator/coroutine functionality using a zero-mutex, stack-based design where weave contexts travel as values rather than through global state. The implementation includes:

  1. Runtime weave operations (strand.weave, strand.resume, yield, strand.weave-cancel)
  2. Type system effect tracking with | Yield T annotations
  3. Comprehensive examples and tests including complex data types

Strengths

1. Excellent Architecture

  • Zero-mutex design: WeaveCtx travels on the stack, eliminating global registry overhead
  • Explicit context threading: Follows the same pattern as channels - values are handles
  • Clean cancellation support: strand.weave-cancel prevents resource leaks
  • The design is consistent with the existing channel model

2. Strong Type System Integration

  • Effect tracking: | Yield T annotations provide compile-time safety (crates/compiler/src/types.rs:13-23)
  • Bidirectional effect checking: Prevents false effect declarations (crates/compiler/src/typechecker.rs:359-369)
  • Effect propagation: Through branches, match expressions, and function calls
  • Type errors guide users to fix missing effect annotations

3. Comprehensive Documentation

  • Excellent module docs in weave.rs explaining zero-mutex design
  • Clear resource management warnings with proper cleanup examples
  • Both examples (counter.seq, sensor-classifier.seq) demonstrate real-world patterns

4. Thorough Testing

  • Integration tests cover basic operations, completion, cancellation, and complex types
  • Records with nested ADTs tested (test-weave-records.seq)
  • Edge cases like immediate completion and pre-resume cancellation covered

🔍 Code Quality Observations

Runtime Implementation (crates/runtime/src/weave.rs)

Good:

  • Clear separation between Quotation and Closure handling
  • Proper ACTIVE_STRANDS tracking for shutdown synchronization
  • Cancel sentinel handling prevents hanging strands

Potential Issues:

  1. Sentinel Value Limitations (lines 84-88)

    • i64::MIN and i64::MIN + 1 cannot be yielded
    • This is documented but could be fragile
    • Suggestion: Consider a more robust completion protocol using a separate control channel or wrapping values in an enum (Result-like) to distinguish data from control signals
  2. Panic on Channel Errors (line 399)

    if yield_chan.sender.send(value_to_send).is_err() {
        panic!("yield: yield channel closed unexpectedly");
    }
    
    • Issue: Panicking in extern "C" functions can cause undefined behavior
    • Mitigation: The code handles cancellation before this, which is good
    • Suggestion: Consider logging and blocking instead of panicking, similar to the cancellation path (lines 416-422)
  3. Unreachable After Blocking (line 422)

    let _ = rx.recv();
    unreachable!("cancelled weave should block forever");
    
    • The comment says "block forever" but then uses unreachable!()
    • Suggestion: Use a loop or remove the unreachable, as the recv() already blocks

Type System (crates/compiler/src/typechecker.rs)

Excellent work on bidirectional effect checking!

  1. Effect Propagation Through Quotations (line 820)

    • Quotations don't propagate effects to outer scope (by design)
    • Effects are captured in the quotation's type
    • Question: Should there be a warning if a quotation has effects but isn't used with strand.weave? Or is strand.spawn meant to reject them?
  2. Effect Merging in Branches (lines 838-845)

    • Uses contains() to deduplicate effects
    • Minor concern: This assumes effects are comparable by value
    • Works for Yield T but may need refinement if more effect types are added
    • Suggestion: Consider using a HashSet or ensuring effects are properly ordered/canonical

Parser (crates/compiler/src/parser.rs)

Clean implementation of effect annotation parsing

  1. Error Messages (line 732)
    • Good error message for empty effects after |
    • Enhancement: Consider adding a suggestion like "Did you mean to remove the '|'?" when effects are empty

🐛 Potential Bugs

1. Resource Leak on Dropped WeaveHandle

  • Location: weave.rs documentation (lines 35-64)
  • Issue: If a user drops a WeaveHandle without resuming to completion or calling strand.weave-cancel, the spawned coroutine hangs forever
  • Current mitigation: Well-documented with examples
  • Suggestion: Consider:
    • A runtime warning/panic on WeaveCtx Drop if not completed
    • Or make the type non-droppable (linear types concept)
    • Or implement Drop to auto-cancel (though this might hide bugs)

2. Stack Overflow in Infinite Loops Without Yield

: bad-loop ( Ctx Int -- | Yield Int )
  counter-loop   # forgot to yield!
;
  • The type checker requires the Yield annotation but doesn't enforce that yield is actually called
  • Impact: Low - this would just be a non-terminating function, which is a logic error
  • Already caught: The bidirectional effect checking at typechecker.rs:359-369 should catch this!

Actually, checking the code more carefully - this is already handled correctly by the bidirectional checking. False alarm!


Performance Considerations

1. Channel Allocation Per Weave

  • Each strand.weave creates two channels (yield_chan, resume_chan)
  • Channels use MPMC which has some allocation overhead
  • Assessment: Acceptable for generator use case - channels are designed for this
  • Optimization idea: Pool channel pairs if weaves are created/destroyed frequently

2. Arc Cloning

  • WeaveCtx contains two Arc<ChannelData>
  • Cloning increments reference counts (atomic operations)
  • Assessment: Minimal overhead, much better than mutex contention
  • The zero-mutex design is excellent here

3. Sentinel Value Checks

  • Every resume checks for DONE_SENTINEL and CANCEL_SENTINEL (lines 340, 409)
  • Assessment: Negligible cost - simple i64 comparison
  • Alternative design with separate control channel would add more overhead

🔒 Security Concerns

1. No Memory Safety Issues

  • Rust's safety guarantees are preserved
  • Unsafe blocks are well-justified for FFI
  • Stack cloning is handled correctly

2. No Panic Across FFI Boundary

  • Most panics are acceptable (they're programming errors)
  • Exception: yield panic at line 399 (discussed above)

3. No Data Races

  • Arc-based sharing is safe
  • MPMC channels handle synchronization
  • No shared mutable state

📊 Test Coverage

Excellent coverage overall:

Basic yield/resume flow (test-weave.seq)
Completion detection
Immediate completion (no yields)
Cancellation before first resume
Cancellation after partial resume
Records with nested ADTs (test-weave-records.seq)
Natural completion with records

Suggestions for additional tests:

  1. Concurrent weaves: Multiple weaves running and interacting
  2. Error propagation: What happens if the weave quotation panics?
  3. Large data yields: Stress test with large values
  4. Type system edge cases:
    • Weave with polymorphic yield type
    • Nested quotations with effects
  5. Performance benchmarks: Time to create/resume/complete weaves

🎨 Style and Best Practices

1. Naming Convention

  • strand.weave, strand.resume, strand.weave-cancel - consistent namespace
  • Clear distinction from strand.spawn

2. Comment Quality

  • Excellent inline documentation
  • Stack effect comments throughout
  • Module-level architecture docs

3. Example Quality

  • Both examples are clear and educational
  • sensor-classifier.seq shows real-world pattern useful for seq-prolog

🚀 Recommendations

High Priority

  1. Strengthen completion protocol to avoid reliance on sentinel values (or document trade-offs more)
  2. Add Drop implementation for WeaveCtx to at least warn about resource leaks

Medium Priority

  1. Enhance effect error messages with hints about strand.weave vs strand.spawn
  2. Add test for panicking weave to ensure graceful handling
  3. Document performance characteristics in module docs

Low Priority

  1. Consider adding yield_all for generator composition (mentioned in issue)
  2. Profile and optimize channel creation if it becomes a bottleneck
  3. Add tracing/debugging support for weave lifecycle

📝 Documentation Suggestions

The documentation is excellent! Minor additions:

  1. Add to README/main docs: Overview of when to use weaves vs channels vs spawn
  2. Performance guide: Expected overhead of weaves
  3. Migration guide: If users have existing channel-based patterns, how to convert to weaves
  4. Type system guide: How effect tracking works with weaves

Final Assessment

Overall: Excellent work! 🎉

This PR demonstrates:

  • Strong architectural thinking (zero-mutex design)
  • Thorough type system integration
  • Excellent documentation and examples
  • Comprehensive testing

The implementation achieves the goals stated in issue #131 and provides a solid foundation for seq-prolog and other generator use cases.

Verdict: APPROVE

The identified issues are minor and mostly suggestions for future improvements. The core implementation is sound, well-tested, and production-ready.

What Makes This PR Stand Out:

  1. Zero-mutex design - elegant and performant
  2. Bidirectional effect checking - prevents false effect declarations
  3. Resource cleanup with explicit cancellation
  4. Iterative development visible in commits - each checkpoint validated by CI

Great work! This is a substantial feature addition that's well-integrated with the existing codebase.

## PR Review: Generator/Coroutine Support (Issue #131) This is an impressive implementation of generator support with strong type system integration. The PR delivers a well-architected solution with excellent documentation and test coverage. --- ## 🎯 Summary This PR implements generator/coroutine functionality using a **zero-mutex, stack-based design** where weave contexts travel as values rather than through global state. The implementation includes: 1. **Runtime weave operations** (`strand.weave`, `strand.resume`, `yield`, `strand.weave-cancel`) 2. **Type system effect tracking** with `| Yield T` annotations 3. **Comprehensive examples and tests** including complex data types --- ## ✅ Strengths ### 1. **Excellent Architecture** - **Zero-mutex design**: WeaveCtx travels on the stack, eliminating global registry overhead - **Explicit context threading**: Follows the same pattern as channels - values are handles - **Clean cancellation support**: `strand.weave-cancel` prevents resource leaks - The design is consistent with the existing channel model ### 2. **Strong Type System Integration** - **Effect tracking**: `| Yield T` annotations provide compile-time safety (crates/compiler/src/types.rs:13-23) - **Bidirectional effect checking**: Prevents false effect declarations (crates/compiler/src/typechecker.rs:359-369) - **Effect propagation**: Through branches, match expressions, and function calls - Type errors guide users to fix missing effect annotations ### 3. **Comprehensive Documentation** - Excellent module docs in weave.rs explaining zero-mutex design - Clear resource management warnings with proper cleanup examples - Both examples (counter.seq, sensor-classifier.seq) demonstrate real-world patterns ### 4. **Thorough Testing** - Integration tests cover basic operations, completion, cancellation, and complex types - Records with nested ADTs tested (test-weave-records.seq) - Edge cases like immediate completion and pre-resume cancellation covered --- ## 🔍 Code Quality Observations ### Runtime Implementation (crates/runtime/src/weave.rs) **Good:** - Clear separation between Quotation and Closure handling - Proper ACTIVE_STRANDS tracking for shutdown synchronization - Cancel sentinel handling prevents hanging strands **Potential Issues:** 1. **Sentinel Value Limitations** (lines 84-88) - `i64::MIN` and `i64::MIN + 1` cannot be yielded - This is documented but could be fragile - **Suggestion**: Consider a more robust completion protocol using a separate control channel or wrapping values in an enum (Result-like) to distinguish data from control signals 2. **Panic on Channel Errors** (line 399) ```rust if yield_chan.sender.send(value_to_send).is_err() { panic!("yield: yield channel closed unexpectedly"); } ``` - **Issue**: Panicking in `extern "C"` functions can cause undefined behavior - **Mitigation**: The code handles cancellation before this, which is good - **Suggestion**: Consider logging and blocking instead of panicking, similar to the cancellation path (lines 416-422) 3. **Unreachable After Blocking** (line 422) ```rust let _ = rx.recv(); unreachable!("cancelled weave should block forever"); ``` - The comment says "block forever" but then uses `unreachable!()` - **Suggestion**: Use a loop or remove the unreachable, as the recv() already blocks ### Type System (crates/compiler/src/typechecker.rs) **Excellent work on bidirectional effect checking!** 4. **Effect Propagation Through Quotations** (line 820) - Quotations don't propagate effects to outer scope (by design) - Effects are captured in the quotation's type - **Question**: Should there be a warning if a quotation has effects but isn't used with `strand.weave`? Or is `strand.spawn` meant to reject them? 5. **Effect Merging in Branches** (lines 838-845) - Uses `contains()` to deduplicate effects - **Minor concern**: This assumes effects are comparable by value - Works for `Yield T` but may need refinement if more effect types are added - **Suggestion**: Consider using a `HashSet` or ensuring effects are properly ordered/canonical ### Parser (crates/compiler/src/parser.rs) **Clean implementation of effect annotation parsing** 6. **Error Messages** (line 732) - Good error message for empty effects after `|` - **Enhancement**: Consider adding a suggestion like "Did you mean to remove the '|'?" when effects are empty --- ## 🐛 Potential Bugs ### 1. **Resource Leak on Dropped WeaveHandle** - **Location**: weave.rs documentation (lines 35-64) - **Issue**: If a user drops a WeaveHandle without resuming to completion or calling `strand.weave-cancel`, the spawned coroutine hangs forever - **Current mitigation**: Well-documented with examples - **Suggestion**: Consider: - A runtime warning/panic on WeaveCtx Drop if not completed - Or make the type non-droppable (linear types concept) - Or implement Drop to auto-cancel (though this might hide bugs) ### 2. **Stack Overflow in Infinite Loops Without Yield** ```seq : bad-loop ( Ctx Int -- | Yield Int ) counter-loop # forgot to yield! ; ``` - The type checker requires the `Yield` annotation but doesn't enforce that yield is actually called - **Impact**: Low - this would just be a non-terminating function, which is a logic error - **Already caught**: The bidirectional effect checking at typechecker.rs:359-369 should catch this! Actually, checking the code more carefully - **this is already handled correctly** by the bidirectional checking. False alarm! ✅ --- ## ⚡ Performance Considerations ### 1. **Channel Allocation Per Weave** - Each `strand.weave` creates two channels (yield_chan, resume_chan) - Channels use MPMC which has some allocation overhead - **Assessment**: Acceptable for generator use case - channels are designed for this - **Optimization idea**: Pool channel pairs if weaves are created/destroyed frequently ### 2. **Arc Cloning** - WeaveCtx contains two `Arc<ChannelData>` - Cloning increments reference counts (atomic operations) - **Assessment**: Minimal overhead, much better than mutex contention - The zero-mutex design is excellent here ### 3. **Sentinel Value Checks** - Every resume checks for DONE_SENTINEL and CANCEL_SENTINEL (lines 340, 409) - **Assessment**: Negligible cost - simple i64 comparison - Alternative design with separate control channel would add more overhead --- ## 🔒 Security Concerns ### 1. **No Memory Safety Issues** - Rust's safety guarantees are preserved - Unsafe blocks are well-justified for FFI - Stack cloning is handled correctly ### 2. **No Panic Across FFI Boundary** - Most panics are acceptable (they're programming errors) - Exception: yield panic at line 399 (discussed above) ### 3. **No Data Races** - Arc-based sharing is safe - MPMC channels handle synchronization - No shared mutable state --- ## 📊 Test Coverage **Excellent coverage overall:** ✅ Basic yield/resume flow (test-weave.seq) ✅ Completion detection ✅ Immediate completion (no yields) ✅ Cancellation before first resume ✅ Cancellation after partial resume ✅ Records with nested ADTs (test-weave-records.seq) ✅ Natural completion with records **Suggestions for additional tests:** 1. **Concurrent weaves**: Multiple weaves running and interacting 2. **Error propagation**: What happens if the weave quotation panics? 3. **Large data yields**: Stress test with large values 4. **Type system edge cases**: - Weave with polymorphic yield type - Nested quotations with effects 5. **Performance benchmarks**: Time to create/resume/complete weaves --- ## 🎨 Style and Best Practices ### 1. **Naming Convention** - `strand.weave`, `strand.resume`, `strand.weave-cancel` - consistent namespace ✅ - Clear distinction from `strand.spawn` ✅ ### 2. **Comment Quality** - Excellent inline documentation - Stack effect comments throughout - Module-level architecture docs ### 3. **Example Quality** - Both examples are clear and educational - sensor-classifier.seq shows real-world pattern useful for seq-prolog ✅ --- ## 🚀 Recommendations ### High Priority 1. **Strengthen completion protocol** to avoid reliance on sentinel values (or document trade-offs more) 2. **Add Drop implementation** for WeaveCtx to at least warn about resource leaks ### Medium Priority 3. **Enhance effect error messages** with hints about strand.weave vs strand.spawn 4. **Add test for panicking weave** to ensure graceful handling 5. **Document performance characteristics** in module docs ### Low Priority 6. **Consider adding yield_all** for generator composition (mentioned in issue) 7. **Profile and optimize** channel creation if it becomes a bottleneck 8. **Add tracing/debugging support** for weave lifecycle --- ## 📝 Documentation Suggestions The documentation is excellent! Minor additions: 1. **Add to README/main docs**: Overview of when to use weaves vs channels vs spawn 2. **Performance guide**: Expected overhead of weaves 3. **Migration guide**: If users have existing channel-based patterns, how to convert to weaves 4. **Type system guide**: How effect tracking works with weaves --- ## ✅ Final Assessment **Overall: Excellent work! 🎉** This PR demonstrates: - Strong architectural thinking (zero-mutex design) - Thorough type system integration - Excellent documentation and examples - Comprehensive testing The implementation achieves the goals stated in issue #131 and provides a solid foundation for seq-prolog and other generator use cases. ### Verdict: **APPROVE** ✅ The identified issues are minor and mostly suggestions for future improvements. The core implementation is sound, well-tested, and production-ready. ### What Makes This PR Stand Out: 1. **Zero-mutex design** - elegant and performant 2. **Bidirectional effect checking** - prevents false effect declarations 3. **Resource cleanup** with explicit cancellation 4. **Iterative development** visible in commits - each checkpoint validated by CI Great work! This is a substantial feature addition that's well-integrated with the existing codebase.
claude[bot] commented 2025-12-30 15:40:18 +00:00 (Migrated from github.com)

Pull Request Review: Generator/Weave Support (Issue #131)

Summary

This PR adds comprehensive generator/coroutine support to Seq through the "weave" mechanism, enabling lazy sequences, streaming, and backtracking patterns. This is a significant and well-architected feature addition that aligns with the repository's goals and implements the feature request from Issue #131.


Strengths

1. Excellent Architecture & Design

  • Zero-mutex design: WeaveCtx travels on the stack rather than using a global registry, eliminating synchronization overhead and following Seq's existing channel design pattern
  • Clean separation of concerns: Weave logic is isolated in crates/runtime/src/weave.rs with clear documentation
  • Bidirectional communication: The yield/resume channel pair design is elegant and mirrors producer-consumer patterns
  • Resource management: Explicit cancellation via strand.weave-cancel prevents resource leaks from abandoned weaves

2. Type System Integration

  • Computational effects: The addition of SideEffect::Yield to the type system (crates/compiler/src/types.rs:12-30) properly tracks generator effects at compile time
  • Effect propagation: Effects correctly propagate through function calls, if/else branches, and match expressions (crates/compiler/src/typechecker.rs)
  • Bidirectional checking: The type checker validates both that inferred effects match declarations AND that declared effects are actually produced (lines 344-367)

3. Documentation Quality

  • Comprehensive documentation in weave.rs explaining the zero-mutex design, API, architecture, and resource management
  • Clear examples in examples/weave/counter.seq and examples/weave/sensor-classifier.seq
  • Good test coverage in tests/integration/src/test-weave.seq and tests/integration/src/test-weave-records.seq

4. Lint Integration

  • Excellent lints added to crates/compiler/src/lints.toml (lines 122-150) to catch common mistakes:
    • weave-immediate-drop: Prevents dropping WeaveHandle without cancellation (error severity)
    • unchecked-resume: Warns about not checking completion status
    • These will help users avoid resource leaks

5. Migration & Backward Compatibility

  • Clean rename of spawn to strand.spawn maintains consistency with new strand.weave API
  • Benchmarks and examples updated systematically (fanout, pingpong, skynet)

🔍 Issues & Concerns

CRITICAL: Sentinel Value Collision Risk

Location: crates/runtime/src/weave.rs:79-88

Using i64::MIN and i64::MIN + 1 as sentinels means users cannot yield these values. While documented (lines 66-71), this is a silent correctness bug - yielding i64::MIN would be SILENTLY interpreted as completion with no error message.

Impact: High - data corruption with no error message

Recommendation: Use a tagged enum or wrapper type instead to eliminate the sentinel collision risk entirely. The performance overhead of matching is negligible compared to the channel send/receive operations.


HIGH: Resource Leak on Panic

Location: crates/runtime/src/weave.rs:398-406

If yield panics, the strand counter is never decremented, causing wait_all_strands to hang forever.

Recommendation: Wrap coroutine execution in a panic guard or use catch_unwind to ensure cleanup_strand() is always called, even on panic.


MEDIUM: Type System - Unchecked Effect Soundness

Location: crates/compiler/src/typechecker.rs:344-367

The bidirectional effect checking is good, but the type system doesn't verify that the Yield effect CAN occur (just that IF it occurs, it's declared). A function that declares | Yield Int but never actually yields is allowed.

Recommendation: This is probably acceptable (conservative typing), but consider adding a lint for "declared but never used" effects if you want stricter checking.


MEDIUM: Missing Tests

While test coverage is good, these scenarios are missing:

  1. Multiple concurrent weaves: Does creating 100+ weaves simultaneously cause any issues?
  2. Weave cancellation during active execution: What happens if strand.weave-cancel is called while the weave is actively executing (not blocked on yield)?
  3. Error propagation: What happens if the quotation panics inside a weave?
  4. Channel closure: What if someone manually drops the yield/resume channels?

Recommendation: Add stress tests and error case tests.


LOW: Parser Effect Annotation Edge Cases

Location: crates/compiler/src/parser.rs:732-776

The parser requires at least one effect after | (line 771). Document the grammar explicitly and add tests for invalid syntax like ( a -- b | ) and consider future extensibility for multiple effects.


LOW: Documentation - Sentinel Value Warning

The sentinel value limitation is documented in weave.rs but NOT in the user-facing language guide or examples.

Recommendation: Add a warning box to docs/language-guide.md or create a docs/WEAVE_GUIDE.md explaining what weaves are, how to use them, the i64::MIN limitation prominently, and resource management best practices.


🎯 Code Quality

Best Practices

  • Consistent naming conventions (strand.weave, strand.resume, strand.weave-cancel)
  • Clear separation between runtime (Rust) and language (Seq) APIs
  • Good use of Rust safety features (Arc, proper Drop semantics)
  • Comments explain WHY, not just WHAT

Minor Style Issues

  1. crates/compiler/src/builtins.rs:467-520 - Some comment formatting inconsistency (minor)
  2. crates/runtime/src/weave.rs:416-422 - The "block forever" approach on cancellation is clever but could use more explanation

🔒 Security

No significant security concerns. The weave mechanism:

  • Doesn't introduce unsafe memory access patterns
  • Properly uses unsafe blocks only where necessary (FFI boundaries)
  • Maintains proper resource cleanup (with the caveat about panic handling)

Performance

Performance looks good:

  • Zero-mutex design eliminates contention
  • Arc-based sharing is efficient
  • No heap allocations in hot paths beyond channel operations

Note: The sentinel value check on every resume adds a branch, but this is unavoidable with the current design. Switching to a tagged enum would make this more explicit without performance penalty.


📝 Test Coverage

Test coverage is solid:

  • Basic yield/resume:
  • Completion detection:
  • Immediate completion (no yields):
  • Cancellation (before and after resume):
  • Records example (realistic use case):

Missing:

  • Stress tests
  • Error cases
  • Concurrent weaves

🚀 Recommendations

Before Merge (Critical)

  1. Fix sentinel value collision - Use tagged enum instead of i64::MIN sentinels
  2. Add panic guards - Ensure cleanup_strand() always runs
  3. Add documentation - Create user-facing docs with sentinel limitation warning

Before Merge (High Priority)

  1. Add missing tests - Concurrent weaves, error cases, edge cases
  2. Document parser grammar - Clarify effect annotation syntax

Future Work (Nice to Have)

  1. Consider adding yield_all for sub-generator delegation
  2. Add a lint for "declared but never produced" effects
  3. Performance profiling of generator-heavy workloads

🎉 Conclusion

This is high-quality work that adds a powerful and well-designed feature to Seq. The architecture is sound, the implementation is clean, and the integration with the type system is excellent.

The sentinel value issue is the main concern - it's a correctness bug waiting to happen in production code. Fixing this to use proper enum tagging would make this PR production-ready.

Recommendation: Request changes to address the sentinel value issue, then approve. The rest of the concerns can be addressed in follow-up PRs.


Reviewed by Claude Code

# Pull Request Review: Generator/Weave Support (Issue #131) ## Summary This PR adds comprehensive generator/coroutine support to Seq through the "weave" mechanism, enabling lazy sequences, streaming, and backtracking patterns. This is a significant and well-architected feature addition that aligns with the repository's goals and implements the feature request from Issue #131. --- ## ✅ Strengths ### 1. **Excellent Architecture & Design** - **Zero-mutex design**: WeaveCtx travels on the stack rather than using a global registry, eliminating synchronization overhead and following Seq's existing channel design pattern - **Clean separation of concerns**: Weave logic is isolated in `crates/runtime/src/weave.rs` with clear documentation - **Bidirectional communication**: The yield/resume channel pair design is elegant and mirrors producer-consumer patterns - **Resource management**: Explicit cancellation via `strand.weave-cancel` prevents resource leaks from abandoned weaves ### 2. **Type System Integration** - **Computational effects**: The addition of `SideEffect::Yield` to the type system (crates/compiler/src/types.rs:12-30) properly tracks generator effects at compile time - **Effect propagation**: Effects correctly propagate through function calls, if/else branches, and match expressions (crates/compiler/src/typechecker.rs) - **Bidirectional checking**: The type checker validates both that inferred effects match declarations AND that declared effects are actually produced (lines 344-367) ### 3. **Documentation Quality** - Comprehensive documentation in `weave.rs` explaining the zero-mutex design, API, architecture, and resource management - Clear examples in `examples/weave/counter.seq` and `examples/weave/sensor-classifier.seq` - Good test coverage in `tests/integration/src/test-weave.seq` and `tests/integration/src/test-weave-records.seq` ### 4. **Lint Integration** - Excellent lints added to `crates/compiler/src/lints.toml` (lines 122-150) to catch common mistakes: - `weave-immediate-drop`: Prevents dropping WeaveHandle without cancellation (error severity) - `unchecked-resume`: Warns about not checking completion status - These will help users avoid resource leaks ### 5. **Migration & Backward Compatibility** - Clean rename of `spawn` to `strand.spawn` maintains consistency with new `strand.weave` API - Benchmarks and examples updated systematically (fanout, pingpong, skynet) --- ## 🔍 Issues & Concerns ### **CRITICAL: Sentinel Value Collision Risk** **Location**: `crates/runtime/src/weave.rs:79-88` Using `i64::MIN` and `i64::MIN + 1` as sentinels means users cannot yield these values. While documented (lines 66-71), this is a silent correctness bug - yielding i64::MIN would be SILENTLY interpreted as completion with no error message. **Impact**: High - data corruption with no error message **Recommendation**: Use a tagged enum or wrapper type instead to eliminate the sentinel collision risk entirely. The performance overhead of matching is negligible compared to the channel send/receive operations. --- ### **HIGH: Resource Leak on Panic** **Location**: `crates/runtime/src/weave.rs:398-406` If `yield` panics, the strand counter is never decremented, causing `wait_all_strands` to hang forever. **Recommendation**: Wrap coroutine execution in a panic guard or use `catch_unwind` to ensure `cleanup_strand()` is always called, even on panic. --- ### **MEDIUM: Type System - Unchecked Effect Soundness** **Location**: `crates/compiler/src/typechecker.rs:344-367` The bidirectional effect checking is good, but the type system doesn't verify that the Yield effect CAN occur (just that IF it occurs, it's declared). A function that declares `| Yield Int` but never actually yields is allowed. **Recommendation**: This is probably acceptable (conservative typing), but consider adding a lint for "declared but never used" effects if you want stricter checking. --- ### **MEDIUM: Missing Tests** While test coverage is good, these scenarios are missing: 1. **Multiple concurrent weaves**: Does creating 100+ weaves simultaneously cause any issues? 2. **Weave cancellation during active execution**: What happens if `strand.weave-cancel` is called while the weave is actively executing (not blocked on yield)? 3. **Error propagation**: What happens if the quotation panics inside a weave? 4. **Channel closure**: What if someone manually drops the yield/resume channels? **Recommendation**: Add stress tests and error case tests. --- ### **LOW: Parser Effect Annotation Edge Cases** **Location**: `crates/compiler/src/parser.rs:732-776` The parser requires at least one effect after `|` (line 771). Document the grammar explicitly and add tests for invalid syntax like `( a -- b | )` and consider future extensibility for multiple effects. --- ### **LOW: Documentation - Sentinel Value Warning** The sentinel value limitation is documented in `weave.rs` but NOT in the user-facing language guide or examples. **Recommendation**: Add a warning box to `docs/language-guide.md` or create a `docs/WEAVE_GUIDE.md` explaining what weaves are, how to use them, the i64::MIN limitation prominently, and resource management best practices. --- ## 🎯 Code Quality ### Best Practices ✅ - Consistent naming conventions (`strand.weave`, `strand.resume`, `strand.weave-cancel`) - Clear separation between runtime (Rust) and language (Seq) APIs - Good use of Rust safety features (`Arc`, proper Drop semantics) - Comments explain WHY, not just WHAT ### Minor Style Issues 1. `crates/compiler/src/builtins.rs:467-520` - Some comment formatting inconsistency (minor) 2. `crates/runtime/src/weave.rs:416-422` - The "block forever" approach on cancellation is clever but could use more explanation --- ## 🔒 Security No significant security concerns. The weave mechanism: - Doesn't introduce unsafe memory access patterns - Properly uses `unsafe` blocks only where necessary (FFI boundaries) - Maintains proper resource cleanup (with the caveat about panic handling) --- ## ⚡ Performance Performance looks good: - Zero-mutex design eliminates contention - Arc-based sharing is efficient - No heap allocations in hot paths beyond channel operations **Note**: The sentinel value check on every resume adds a branch, but this is unavoidable with the current design. Switching to a tagged enum would make this more explicit without performance penalty. --- ## 📝 Test Coverage Test coverage is solid: - Basic yield/resume: ✅ - Completion detection: ✅ - Immediate completion (no yields): ✅ - Cancellation (before and after resume): ✅ - Records example (realistic use case): ✅ **Missing**: - Stress tests - Error cases - Concurrent weaves --- ## 🚀 Recommendations ### Before Merge (Critical) 1. **Fix sentinel value collision** - Use tagged enum instead of i64::MIN sentinels 2. **Add panic guards** - Ensure cleanup_strand() always runs 3. **Add documentation** - Create user-facing docs with sentinel limitation warning ### Before Merge (High Priority) 4. **Add missing tests** - Concurrent weaves, error cases, edge cases 5. **Document parser grammar** - Clarify effect annotation syntax ### Future Work (Nice to Have) 6. Consider adding `yield_all` for sub-generator delegation 7. Add a lint for "declared but never produced" effects 8. Performance profiling of generator-heavy workloads --- ## 🎉 Conclusion This is **high-quality work** that adds a powerful and well-designed feature to Seq. The architecture is sound, the implementation is clean, and the integration with the type system is excellent. The sentinel value issue is the main concern - it's a correctness bug waiting to happen in production code. Fixing this to use proper enum tagging would make this PR production-ready. **Recommendation**: Request changes to address the sentinel value issue, then approve. The rest of the concerns can be addressed in follow-up PRs. --- **Reviewed by Claude Code**
navicore commented 2025-12-30 15:46:08 +00:00 (Migrated from github.com)
addressing concerns in separate PRs see 1. https://github.com/navicore/patch-seq/issues/140 2. https://github.com/navicore/patch-seq/issues/139
Sign in to join this conversation.
No description provided.