Feature: Generator support with yield_value and resume #131
Labels
No labels
bug
dependencies
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
refactor
rust
technical-debt
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
navicore/patch-seq#131
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Add generator/coroutine support to Seq with the ability to yield values to callers and resume execution. This enables lazy sequences, streaming, and backtracking patterns.
Motivation
Primary Use Case: seq-prolog
We're building a compiled Prolog interpreter in Seq (seq-prolog). Prolog's backtracking naturally maps to generators:
;Without generators, we'd need workarounds:
General Use Cases
Generators are a fundamental pattern used across languages (Python, JavaScript, Go, Rust iterators, C#, Lua):
Proposed Semantics
Creating a Generator
Using a Generator
Generator State
For-each Style Iteration
Implementation Notes
Leveraging May
Seq already uses may for green threads. May's coroutines support suspend/resume, which could back generators:
Key question: Does may's
coroutine::yield_with()or similar provide value-passing on yield? If not, we may need a small wrapper using channels internally, but hidden from the Seq user.Stack Considerations
When a generator yields:
This is similar to how
spawnclones the stack, but generators maintain a single suspended stack rather than running concurrently.Interaction with TCO
Generators should work with Seq's tail-call optimization. A generator that tail-calls itself should not grow the stack:
Alternatives Considered
Use channels: Works but adds overhead. Good for concurrent producers, overkill for sequential iteration.
CPS transformation: Compile all code to continuation-passing style. Complex and invasive.
Explicit choice points: Manual state management. Essentially an interpreter.
Generators provide the cleanest abstraction for "produce values on demand" patterns.
Related
Questions to Explore
yield_allfor delegating to sub-generators?Design Summary: Weaves (Generators for Seq)
Naming
Weave - Continuing the textile metaphor (strands are threads of execution), a weave is a strand you can interleave with, yielding control back and forth. Captures the bidirectional, interwoven nature.
Core Semantics
Result Type (follows std:result convention)
Error Handling: No Panics
Resuming a completed weave returns
Done, not a panic. Caller must handle it:Or using match:
Implementation Approach
Build on existing strand/channel infrastructure:
weave.makespawns a may coroutine, parks waiting on internal resume channelweave.yieldsends to yield channel, blocks on resume channelweave.resumesends to resume channel, receives from yield channelDonesentinelExample: Counter
Example: Prolog-style backtracking
Related: Error Handling Audit
This design revealed that some existing builtins use panics where they should return Results:
chan.receivepanics on closed channel → should return Resultchan.sendpanics on closed channel → should return Result-safevariants exist but the unsafe versions shouldn't crashProposed follow-up issue: Audit all builtins and convert panics to Result returns. The
-safesuffix pattern is backwards - safe should be the default, with explicit-unsafe!or-unchecked!variants for performance-critical code that's willing to crash.Design Evolution: Unified Strands with Effect Tracking
After further discussion, we've evolved the design to avoid introducing a separate "Weave" type. Instead, generators are strands with the Yield effect - one concurrency primitive, two modes of use.
Core Insight
Strands and generators aren't fundamentally different:
The difference is the communication pattern:
spawn: fire-and-forget, communicate via explicit channelsweave: structured yield/resume protocolEffect-Annotated Quotation Types
Seq already tracks stack effects:
( a b -- a c ). We extend this to track computational effects:The
|separates stack effects from computational effects. This extends naturally from Seq's existing row polymorphism.Strand Operations
Compile-Time Safety
The effect system catches errors at compile time:
Effect Propagation
Effects bubble up through function calls:
A pure
mainwith unhandledYieldeffects = compile error.Future Effect Extensions
The same mechanism could track other effects:
But for now, just
Yield Tis the focus.Why This Approach
Implementation Considerations
Dependency: Should complete error handling audit (#132) first, so the new strand operations return Results consistently.
https://github.com/navicore/patch-seq/pull/138
https://github.com/navicore/patch-seq/pull/138